/* * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008 David Smith * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef NodeRareData_h #define NodeRareData_h #include "ChildNodeList.h" #include "ClassCollection.h" #include "DOMTokenList.h" #include "HTMLCollection.h" #include "HTMLNames.h" #include "LiveNodeList.h" #include "MutationObserver.h" #include "MutationObserverRegistration.h" #include "Page.h" #include "QualifiedName.h" #include "TagCollection.h" #include #include #if ENABLE(VIDEO_TRACK) #include "TextTrack.h" #endif namespace WebCore { class LabelsNodeList; class RadioNodeList; class TreeScope; template struct NodeListTypeIdentifier; template <> struct NodeListTypeIdentifier { static int value() { return 0; } }; template <> struct NodeListTypeIdentifier { static int value() { return 1; } }; template <> struct NodeListTypeIdentifier { static int value() { return 2; } }; class NodeListsNodeData { WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED; public: NodeListsNodeData() : m_childNodeList(nullptr) , m_emptyChildNodeList(nullptr) { } void clearChildNodeListCache() { if (m_childNodeList) m_childNodeList->invalidateCache(); } Ref ensureChildNodeList(ContainerNode& node) { ASSERT(!m_emptyChildNodeList); if (m_childNodeList) return *m_childNodeList; auto list = ChildNodeList::create(node); m_childNodeList = list.ptr(); return list; } void removeChildNodeList(ChildNodeList* list) { ASSERT(m_childNodeList == list); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) return; m_childNodeList = nullptr; } Ref ensureEmptyChildNodeList(Node& node) { ASSERT(!m_childNodeList); if (m_emptyChildNodeList) return *m_emptyChildNodeList; auto list = EmptyNodeList::create(node); m_emptyChildNodeList = list.ptr(); return list; } void removeEmptyChildNodeList(EmptyNodeList* list) { ASSERT(m_emptyChildNodeList == list); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) return; m_emptyChildNodeList = nullptr; } struct NodeListCacheMapEntryHash { static unsigned hash(const std::pair& entry) { return DefaultHash::Hash::hash(entry.second) + entry.first; } static bool equal(const std::pair& a, const std::pair& b) { return a.first == b.first && DefaultHash::Hash::equal(a.second, b.second); } static const bool safeToCompareToEmptyOrDeleted = DefaultHash::Hash::safeToCompareToEmptyOrDeleted; }; typedef HashMap, LiveNodeList*, NodeListCacheMapEntryHash> NodeListAtomicNameCacheMap; typedef HashMap, HTMLCollection*, NodeListCacheMapEntryHash> CollectionCacheMap; typedef HashMap TagCollectionCacheNS; template ALWAYS_INLINE Ref addCacheWithAtomicName(ContainerType& container, const AtomicString& name) { NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.fastAdd(namedNodeListKey(name), nullptr); if (!result.isNewEntry) return static_cast(*result.iterator->value); auto list = T::create(container, name); result.iterator->value = &list.get(); return list; } ALWAYS_INLINE Ref addCachedCollectionWithQualifiedName(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName) { QualifiedName name(nullAtom, localName, namespaceURI); TagCollectionCacheNS::AddResult result = m_tagCollectionCacheNS.fastAdd(name, nullptr); if (!result.isNewEntry) return *result.iterator->value; auto list = TagCollection::create(node, namespaceURI, localName); result.iterator->value = list.ptr(); return list; } template ALWAYS_INLINE Ref addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name) { CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, name), nullptr); if (!result.isNewEntry) return static_cast(*result.iterator->value); auto list = T::create(container, collectionType, name); result.iterator->value = &list.get(); return list; } template ALWAYS_INLINE Ref addCachedCollection(ContainerType& container, CollectionType collectionType) { CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, starAtom), nullptr); if (!result.isNewEntry) return static_cast(*result.iterator->value); auto list = T::create(container, collectionType); result.iterator->value = &list.get(); return list; } template T* cachedCollection(CollectionType collectionType) { return static_cast(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom))); } template void removeCacheWithAtomicName(NodeListType* list, const AtomicString& name = starAtom) { ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(name))); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) return; m_atomicNameCaches.remove(namedNodeListKey(name)); } void removeCachedCollectionWithQualifiedName(HTMLCollection& collection, const AtomicString& namespaceURI, const AtomicString& localName) { QualifiedName name(nullAtom, localName, namespaceURI); ASSERT(&collection == m_tagCollectionCacheNS.get(name)); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection.ownerNode())) return; m_tagCollectionCacheNS.remove(name); } void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom) { ASSERT(collection == m_cachedCollections.get(namedCollectionKey(collection->type(), name))); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection->ownerNode())) return; m_cachedCollections.remove(namedCollectionKey(collection->type(), name)); } void invalidateCaches(const QualifiedName* attrName = nullptr); bool isEmpty() const { return m_atomicNameCaches.isEmpty() && m_cachedCollections.isEmpty() && m_tagCollectionCacheNS.isEmpty(); } void adoptTreeScope() { invalidateCaches(); } void adoptDocument(Document* oldDocument, Document* newDocument) { ASSERT(oldDocument); if (oldDocument == newDocument) { invalidateCaches(); return; } for (auto& cache : m_atomicNameCaches.values()) cache->invalidateCache(*oldDocument); for (auto& list : m_tagCollectionCacheNS.values()) { ASSERT(!list->isRootedAtDocument()); list->invalidateCache(*oldDocument); } for (auto& collection : m_cachedCollections.values()) collection->invalidateCache(*oldDocument); } private: std::pair namedCollectionKey(CollectionType type, const AtomicString& name) { return std::pair(type, name); } template std::pair namedNodeListKey(const AtomicString& name) { return std::pair(NodeListTypeIdentifier::value(), name); } bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&); // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway. ChildNodeList* m_childNodeList; EmptyNodeList* m_emptyChildNodeList; NodeListAtomicNameCacheMap m_atomicNameCaches; TagCollectionCacheNS m_tagCollectionCacheNS; CollectionCacheMap m_cachedCollections; }; class NodeMutationObserverData { WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED; public: Vector> registry; HashSet transientRegistry; NodeMutationObserverData() { } }; class NodeRareData : public NodeRareDataBase { WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED; public: NodeRareData(RenderObject* renderer) : NodeRareDataBase(renderer) , m_connectedFrameCount(0) { } void clearNodeLists() { m_nodeLists = nullptr; } NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } NodeListsNodeData& ensureNodeLists() { if (!m_nodeLists) m_nodeLists = std::make_unique(); return *m_nodeLists; } NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); } NodeMutationObserverData& ensureMutationObserverData() { if (!m_mutationObserverData) m_mutationObserverData = std::make_unique(); return *m_mutationObserverData; } unsigned connectedSubframeCount() const { return m_connectedFrameCount; } void incrementConnectedSubframeCount(unsigned amount) { m_connectedFrameCount += amount; } void decrementConnectedSubframeCount(unsigned amount) { ASSERT(m_connectedFrameCount); ASSERT(amount <= m_connectedFrameCount); m_connectedFrameCount -= amount; } private: unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames. std::unique_ptr m_nodeLists; std::unique_ptr m_mutationObserverData; }; inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode) { ASSERT(ownerNode.nodeLists() == this); if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_tagCollectionCacheNS.size() + m_cachedCollections.size() != 1) return false; ownerNode.clearNodeLists(); return true; } inline NodeRareData* Node::rareData() const { ASSERT_WITH_SECURITY_IMPLICATION(hasRareData()); return static_cast(m_data.m_rareData); } inline NodeRareData& Node::ensureRareData() { if (!hasRareData()) materializeRareData(); return *rareData(); } // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow static_assert(Page::maxNumberOfFrames < 1024, "Frame limit should fit in rare data count"); } // namespace WebCore #endif // NodeRareData_h