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 "ChildNodeList.h" 26#include "DOMSettableTokenList.h" 27#include "HTMLNames.h" 28#include "LiveNodeList.h" 29#include "MutationObserver.h" 30#include "MutationObserverRegistration.h" 31#include "Page.h" 32#include "QualifiedName.h" 33#include "TagNodeList.h" 34#if ENABLE(VIDEO_TRACK) 35#include "TextTrack.h" 36#endif 37#include <wtf/HashSet.h> 38#include <wtf/OwnPtr.h> 39#include <wtf/PassOwnPtr.h> 40#include <wtf/text/AtomicString.h> 41#include <wtf/text/StringHash.h> 42 43#if ENABLE(MICRODATA) 44#include "HTMLPropertiesCollection.h" 45#include "MicroDataAttributeTokenList.h" 46#include "MicroDataItemList.h" 47#endif 48 49namespace WebCore { 50 51class LabelsNodeList; 52class RadioNodeList; 53class TreeScope; 54 55class NodeListsNodeData { 56 WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED; 57public: 58 void clearChildNodeListCache() 59 { 60 if (m_childNodeList) 61 m_childNodeList->invalidateCache(); 62 } 63 64 PassRefPtr<ChildNodeList> ensureChildNodeList(Node* node) 65 { 66 if (m_childNodeList) 67 return m_childNodeList; 68 RefPtr<ChildNodeList> list = ChildNodeList::create(node); 69 m_childNodeList = list.get(); 70 return list.release(); 71 } 72 73 void removeChildNodeList(ChildNodeList* list) 74 { 75 ASSERT(m_childNodeList = list); 76 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 77 return; 78 m_childNodeList = 0; 79 } 80 81 template <typename StringType> 82 struct NodeListCacheMapEntryHash { 83 static unsigned hash(const std::pair<unsigned char, StringType>& entry) 84 { 85 return DefaultHash<StringType>::Hash::hash(entry.second) + entry.first; 86 } 87 static bool equal(const std::pair<unsigned char, StringType>& a, const std::pair<unsigned char, StringType>& b) { return a == b; } 88 static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringType>::Hash::safeToCompareToEmptyOrDeleted; 89 }; 90 91 typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeListBase*, NodeListCacheMapEntryHash<AtomicString> > NodeListAtomicNameCacheMap; 92 typedef HashMap<std::pair<unsigned char, String>, LiveNodeListBase*, NodeListCacheMapEntryHash<String> > NodeListNameCacheMap; 93 typedef HashMap<QualifiedName, TagNodeList*> TagNodeListCacheNS; 94 95 template<typename T> 96 PassRefPtr<T> addCacheWithAtomicName(Node* node, CollectionType collectionType, const AtomicString& name) 97 { 98 NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, name), 0); 99 if (!result.isNewEntry) 100 return static_cast<T*>(result.iterator->value); 101 102 RefPtr<T> list = T::create(node, collectionType, name); 103 result.iterator->value = list.get(); 104 return list.release(); 105 } 106 107 // FIXME: This function should be renamed since it doesn't have an atomic name. 108 template<typename T> 109 PassRefPtr<T> addCacheWithAtomicName(Node* node, CollectionType collectionType) 110 { 111 NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, starAtom), 0); 112 if (!result.isNewEntry) 113 return static_cast<T*>(result.iterator->value); 114 115 RefPtr<T> list = T::create(node, collectionType); 116 result.iterator->value = list.get(); 117 return list.release(); 118 } 119 120 template<typename T> 121 T* cacheWithAtomicName(CollectionType collectionType) 122 { 123 return static_cast<T*>(m_atomicNameCaches.get(namedNodeListKey(collectionType, starAtom))); 124 } 125 126 template<typename T> 127 PassRefPtr<T> addCacheWithName(Node* node, CollectionType collectionType, const String& name) 128 { 129 NodeListNameCacheMap::AddResult result = m_nameCaches.add(namedNodeListKey(collectionType, name), 0); 130 if (!result.isNewEntry) 131 return static_cast<T*>(result.iterator->value); 132 133 RefPtr<T> list = T::create(node, name); 134 result.iterator->value = list.get(); 135 return list.release(); 136 } 137 138 PassRefPtr<TagNodeList> addCacheWithQualifiedName(Node* node, const AtomicString& namespaceURI, const AtomicString& localName) 139 { 140 QualifiedName name(nullAtom, localName, namespaceURI); 141 TagNodeListCacheNS::AddResult result = m_tagNodeListCacheNS.add(name, 0); 142 if (!result.isNewEntry) 143 return result.iterator->value; 144 145 RefPtr<TagNodeList> list = TagNodeList::create(node, namespaceURI, localName); 146 result.iterator->value = list.get(); 147 return list.release(); 148 } 149 150 void removeCacheWithAtomicName(LiveNodeListBase* list, CollectionType collectionType, const AtomicString& name = starAtom) 151 { 152 ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(collectionType, name))); 153 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 154 return; 155 m_atomicNameCaches.remove(namedNodeListKey(collectionType, name)); 156 } 157 158 void removeCacheWithName(LiveNodeListBase* list, CollectionType collectionType, const String& name) 159 { 160 ASSERT(list == m_nameCaches.get(namedNodeListKey(collectionType, name))); 161 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 162 return; 163 m_nameCaches.remove(namedNodeListKey(collectionType, name)); 164 } 165 166 void removeCacheWithQualifiedName(LiveNodeList* list, const AtomicString& namespaceURI, const AtomicString& localName) 167 { 168 QualifiedName name(nullAtom, localName, namespaceURI); 169 ASSERT(list == m_tagNodeListCacheNS.get(name)); 170 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 171 return; 172 m_tagNodeListCacheNS.remove(name); 173 } 174 175 static PassOwnPtr<NodeListsNodeData> create() 176 { 177 return adoptPtr(new NodeListsNodeData); 178 } 179 180 void invalidateCaches(const QualifiedName* attrName = 0); 181 bool isEmpty() const 182 { 183 return m_atomicNameCaches.isEmpty() && m_nameCaches.isEmpty() && m_tagNodeListCacheNS.isEmpty(); 184 } 185 186 void adoptTreeScope() 187 { 188 invalidateCaches(); 189 } 190 191 void adoptDocument(Document* oldDocument, Document* newDocument) 192 { 193 invalidateCaches(); 194 195 if (oldDocument != newDocument) { 196 NodeListAtomicNameCacheMap::const_iterator atomicNameCacheEnd = m_atomicNameCaches.end(); 197 for (NodeListAtomicNameCacheMap::const_iterator it = m_atomicNameCaches.begin(); it != atomicNameCacheEnd; ++it) { 198 LiveNodeListBase* list = it->value; 199 oldDocument->unregisterNodeList(list); 200 newDocument->registerNodeList(list); 201 } 202 203 NodeListNameCacheMap::const_iterator nameCacheEnd = m_nameCaches.end(); 204 for (NodeListNameCacheMap::const_iterator it = m_nameCaches.begin(); it != nameCacheEnd; ++it) { 205 LiveNodeListBase* list = it->value; 206 oldDocument->unregisterNodeList(list); 207 newDocument->registerNodeList(list); 208 } 209 210 TagNodeListCacheNS::const_iterator tagEnd = m_tagNodeListCacheNS.end(); 211 for (TagNodeListCacheNS::const_iterator it = m_tagNodeListCacheNS.begin(); it != tagEnd; ++it) { 212 LiveNodeListBase* list = it->value; 213 ASSERT(!list->isRootedAtDocument()); 214 oldDocument->unregisterNodeList(list); 215 newDocument->registerNodeList(list); 216 } 217 } 218 } 219 220private: 221 NodeListsNodeData() 222 : m_childNodeList(0) 223 { } 224 225 std::pair<unsigned char, AtomicString> namedNodeListKey(CollectionType type, const AtomicString& name) 226 { 227 return std::pair<unsigned char, AtomicString>(type, name); 228 } 229 230 std::pair<unsigned char, String> namedNodeListKey(CollectionType type, const String& name) 231 { 232 return std::pair<unsigned char, String>(type, name); 233 } 234 235 bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node*); 236 237 // FIXME: m_childNodeList should be merged into m_atomicNameCaches or at least be shared with HTMLCollection returned by Element::children 238 // but it's tricky because invalidateCaches shouldn't invalidate this cache and adoptTreeScope shouldn't call registerNodeList or unregisterNodeList. 239 ChildNodeList* m_childNodeList; 240 NodeListAtomicNameCacheMap m_atomicNameCaches; 241 NodeListNameCacheMap m_nameCaches; 242 TagNodeListCacheNS m_tagNodeListCacheNS; 243}; 244 245class NodeMutationObserverData { 246 WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED; 247public: 248 Vector<OwnPtr<MutationObserverRegistration> > registry; 249 HashSet<MutationObserverRegistration*> transientRegistry; 250 251 static PassOwnPtr<NodeMutationObserverData> create() { return adoptPtr(new NodeMutationObserverData); } 252 253private: 254 NodeMutationObserverData() { } 255}; 256 257#if ENABLE(MICRODATA) 258class NodeMicroDataTokenLists { 259 WTF_MAKE_NONCOPYABLE(NodeMicroDataTokenLists); WTF_MAKE_FAST_ALLOCATED; 260public: 261 static PassOwnPtr<NodeMicroDataTokenLists> create() { return adoptPtr(new NodeMicroDataTokenLists); } 262 263 MicroDataAttributeTokenList* itemProp(Node* node) const 264 { 265 if (!m_itemProp) 266 m_itemProp = MicroDataAttributeTokenList::create(toElement(node), HTMLNames::itempropAttr); 267 return m_itemProp.get(); 268 } 269 270 MicroDataAttributeTokenList* itemRef(Node* node) const 271 { 272 if (!m_itemRef) 273 m_itemRef = MicroDataAttributeTokenList::create(toElement(node), HTMLNames::itemrefAttr); 274 return m_itemRef.get(); 275 } 276 277 MicroDataAttributeTokenList* itemType(Node* node) const 278 { 279 if (!m_itemType) 280 m_itemType = MicroDataAttributeTokenList::create(toElement(node), HTMLNames::itemtypeAttr); 281 return m_itemType.get(); 282 } 283 284private: 285 NodeMicroDataTokenLists() { } 286 287 mutable RefPtr<MicroDataAttributeTokenList> m_itemProp; 288 mutable RefPtr<MicroDataAttributeTokenList> m_itemRef; 289 mutable RefPtr<MicroDataAttributeTokenList> m_itemType; 290}; 291#endif 292 293class NodeRareData : public NodeRareDataBase { 294 WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED; 295public: 296 static PassOwnPtr<NodeRareData> create(RenderObject* renderer) { return adoptPtr(new NodeRareData(renderer)); } 297 298 void clearNodeLists() { m_nodeLists.clear(); } 299 NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } 300 NodeListsNodeData* ensureNodeLists() 301 { 302 if (!m_nodeLists) 303 m_nodeLists = NodeListsNodeData::create(); 304 return m_nodeLists.get(); 305 } 306 307 NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); } 308 NodeMutationObserverData* ensureMutationObserverData() 309 { 310 if (!m_mutationObserverData) 311 m_mutationObserverData = NodeMutationObserverData::create(); 312 return m_mutationObserverData.get(); 313 } 314 315#if ENABLE(MICRODATA) 316 NodeMicroDataTokenLists* ensureMicroDataTokenLists() const 317 { 318 if (!m_microDataTokenLists) 319 m_microDataTokenLists = NodeMicroDataTokenLists::create(); 320 return m_microDataTokenLists.get(); 321 } 322#endif 323 324 unsigned connectedSubframeCount() const { return m_connectedFrameCount; } 325 void incrementConnectedSubframeCount(unsigned amount) 326 { 327 m_connectedFrameCount += amount; 328 } 329 void decrementConnectedSubframeCount(unsigned amount) 330 { 331 ASSERT(m_connectedFrameCount); 332 ASSERT(amount <= m_connectedFrameCount); 333 m_connectedFrameCount -= amount; 334 } 335 336protected: 337 NodeRareData(RenderObject* renderer) 338 : NodeRareDataBase(renderer) 339 , m_connectedFrameCount(0) 340 { } 341 342private: 343 unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames. 344 345 OwnPtr<NodeListsNodeData> m_nodeLists; 346 OwnPtr<NodeMutationObserverData> m_mutationObserverData; 347 348#if ENABLE(MICRODATA) 349 mutable OwnPtr<NodeMicroDataTokenLists> m_microDataTokenLists; 350#endif 351}; 352 353inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node* ownerNode) 354{ 355 ASSERT(ownerNode); 356 ASSERT(ownerNode->nodeLists() == this); 357 if ((m_childNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size() + m_tagNodeListCacheNS.size() != 1) 358 return false; 359 ownerNode->clearNodeLists(); 360 return true; 361} 362 363// Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow 364COMPILE_ASSERT(Page::maxNumberOfFrames < 1024, Frame_limit_should_fit_in_rare_data_count); 365 366} // namespace WebCore 367 368#endif // NodeRareData_h 369