| 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 | #pragma once |
| 23 | |
| 24 | #include "ChildNodeList.h" |
| 25 | #include "HTMLCollection.h" |
| 26 | #include "HTMLNames.h" |
| 27 | #include "LiveNodeList.h" |
| 28 | #include "MutationObserverRegistration.h" |
| 29 | #include "QualifiedName.h" |
| 30 | #include "TagCollection.h" |
| 31 | #include <wtf/HashSet.h> |
| 32 | #include <wtf/text/AtomicString.h> |
| 33 | |
| 34 | namespace WebCore { |
| 35 | |
| 36 | class LabelsNodeList; |
| 37 | class NameNodeList; |
| 38 | class RadioNodeList; |
| 39 | class TreeScope; |
| 40 | |
| 41 | template <class ListType> struct NodeListTypeIdentifier; |
| 42 | template <> struct NodeListTypeIdentifier<NameNodeList> { static int value() { return 0; } }; |
| 43 | template <> struct NodeListTypeIdentifier<RadioNodeList> { static int value() { return 1; } }; |
| 44 | template <> struct NodeListTypeIdentifier<LabelsNodeList> { static int value() { return 2; } }; |
| 45 | |
| 46 | class NodeListsNodeData { |
| 47 | WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED; |
| 48 | public: |
| 49 | NodeListsNodeData() |
| 50 | : m_childNodeList(nullptr) |
| 51 | , m_emptyChildNodeList(nullptr) |
| 52 | { |
| 53 | } |
| 54 | |
| 55 | void clearChildNodeListCache() |
| 56 | { |
| 57 | if (m_childNodeList) |
| 58 | m_childNodeList->invalidateCache(); |
| 59 | } |
| 60 | |
| 61 | Ref<ChildNodeList> ensureChildNodeList(ContainerNode& node) |
| 62 | { |
| 63 | ASSERT(!m_emptyChildNodeList); |
| 64 | if (m_childNodeList) |
| 65 | return *m_childNodeList; |
| 66 | auto list = ChildNodeList::create(node); |
| 67 | m_childNodeList = list.ptr(); |
| 68 | return list; |
| 69 | } |
| 70 | |
| 71 | void removeChildNodeList(ChildNodeList* list) |
| 72 | { |
| 73 | ASSERT(m_childNodeList == list); |
| 74 | if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) |
| 75 | return; |
| 76 | m_childNodeList = nullptr; |
| 77 | } |
| 78 | |
| 79 | Ref<EmptyNodeList> ensureEmptyChildNodeList(Node& node) |
| 80 | { |
| 81 | ASSERT(!m_childNodeList); |
| 82 | if (m_emptyChildNodeList) |
| 83 | return *m_emptyChildNodeList; |
| 84 | auto list = EmptyNodeList::create(node); |
| 85 | m_emptyChildNodeList = list.ptr(); |
| 86 | return list; |
| 87 | } |
| 88 | |
| 89 | void removeEmptyChildNodeList(EmptyNodeList* list) |
| 90 | { |
| 91 | ASSERT(m_emptyChildNodeList == list); |
| 92 | if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) |
| 93 | return; |
| 94 | m_emptyChildNodeList = nullptr; |
| 95 | } |
| 96 | |
| 97 | struct NodeListCacheMapEntryHash { |
| 98 | static unsigned hash(const std::pair<unsigned char, AtomicString>& entry) |
| 99 | { |
| 100 | return DefaultHash<AtomicString>::Hash::hash(entry.second) + entry.first; |
| 101 | } |
| 102 | static bool equal(const std::pair<unsigned char, AtomicString>& a, const std::pair<unsigned char, AtomicString>& b) { return a.first == b.first && DefaultHash<AtomicString>::Hash::equal(a.second, b.second); } |
| 103 | static const bool safeToCompareToEmptyOrDeleted = DefaultHash<AtomicString>::Hash::safeToCompareToEmptyOrDeleted; |
| 104 | }; |
| 105 | |
| 106 | typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash> NodeListAtomicNameCacheMap; |
| 107 | typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash> CollectionCacheMap; |
| 108 | typedef HashMap<QualifiedName, TagCollectionNS*> TagCollectionNSCache; |
| 109 | |
| 110 | template<typename T, typename ContainerType> |
| 111 | ALWAYS_INLINE Ref<T> addCacheWithAtomicName(ContainerType& container, const AtomicString& name) |
| 112 | { |
| 113 | NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.fastAdd(namedNodeListKey<T>(name), nullptr); |
| 114 | if (!result.isNewEntry) |
| 115 | return static_cast<T&>(*result.iterator->value); |
| 116 | |
| 117 | auto list = T::create(container, name); |
| 118 | result.iterator->value = &list.get(); |
| 119 | return list; |
| 120 | } |
| 121 | |
| 122 | ALWAYS_INLINE Ref<TagCollectionNS> addCachedTagCollectionNS(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName) |
| 123 | { |
| 124 | QualifiedName name(nullAtom(), localName, namespaceURI); |
| 125 | TagCollectionNSCache::AddResult result = m_tagCollectionNSCache.fastAdd(name, nullptr); |
| 126 | if (!result.isNewEntry) |
| 127 | return *result.iterator->value; |
| 128 | |
| 129 | auto list = TagCollectionNS::create(node, namespaceURI, localName); |
| 130 | result.iterator->value = list.ptr(); |
| 131 | return list; |
| 132 | } |
| 133 | |
| 134 | template<typename T, typename ContainerType> |
| 135 | ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name) |
| 136 | { |
| 137 | CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, name), nullptr); |
| 138 | if (!result.isNewEntry) |
| 139 | return static_cast<T&>(*result.iterator->value); |
| 140 | |
| 141 | auto list = T::create(container, collectionType, name); |
| 142 | result.iterator->value = &list.get(); |
| 143 | return list; |
| 144 | } |
| 145 | |
| 146 | template<typename T, typename ContainerType> |
| 147 | ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType) |
| 148 | { |
| 149 | CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, starAtom()), nullptr); |
| 150 | if (!result.isNewEntry) |
| 151 | return static_cast<T&>(*result.iterator->value); |
| 152 | |
| 153 | auto list = T::create(container, collectionType); |
| 154 | result.iterator->value = &list.get(); |
| 155 | return list; |
| 156 | } |
| 157 | |
| 158 | template<typename T> |
| 159 | T* cachedCollection(CollectionType collectionType) |
| 160 | { |
| 161 | return static_cast<T*>(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom()))); |
| 162 | } |
| 163 | |
| 164 | template <class NodeListType> |
| 165 | void removeCacheWithAtomicName(NodeListType* list, const AtomicString& name = starAtom()) |
| 166 | { |
| 167 | ASSERT(list == m_atomicNameCaches.get(namedNodeListKey<NodeListType>(name))); |
| 168 | if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) |
| 169 | return; |
| 170 | m_atomicNameCaches.remove(namedNodeListKey<NodeListType>(name)); |
| 171 | } |
| 172 | |
| 173 | void removeCachedTagCollectionNS(HTMLCollection& collection, const AtomicString& namespaceURI, const AtomicString& localName) |
| 174 | { |
| 175 | QualifiedName name(nullAtom(), localName, namespaceURI); |
| 176 | ASSERT(&collection == m_tagCollectionNSCache.get(name)); |
| 177 | if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection.ownerNode())) |
| 178 | return; |
| 179 | m_tagCollectionNSCache.remove(name); |
| 180 | } |
| 181 | |
| 182 | void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom()) |
| 183 | { |
| 184 | ASSERT(collection == m_cachedCollections.get(namedCollectionKey(collection->type(), name))); |
| 185 | if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection->ownerNode())) |
| 186 | return; |
| 187 | m_cachedCollections.remove(namedCollectionKey(collection->type(), name)); |
| 188 | } |
| 189 | |
| 190 | void invalidateCaches(); |
| 191 | void invalidateCachesForAttribute(const QualifiedName& attrName); |
| 192 | |
| 193 | void adoptTreeScope() |
| 194 | { |
| 195 | invalidateCaches(); |
| 196 | } |
| 197 | |
| 198 | void adoptDocument(Document& oldDocument, Document& newDocument) |
| 199 | { |
| 200 | if (&oldDocument == &newDocument) { |
| 201 | invalidateCaches(); |
| 202 | return; |
| 203 | } |
| 204 | |
| 205 | for (auto& cache : m_atomicNameCaches.values()) |
| 206 | cache->invalidateCacheForDocument(oldDocument); |
| 207 | |
| 208 | for (auto& list : m_tagCollectionNSCache.values()) { |
| 209 | ASSERT(!list->isRootedAtDocument()); |
| 210 | list->invalidateCacheForDocument(oldDocument); |
| 211 | } |
| 212 | |
| 213 | for (auto& collection : m_cachedCollections.values()) |
| 214 | collection->invalidateCacheForDocument(oldDocument); |
| 215 | } |
| 216 | |
| 217 | private: |
| 218 | std::pair<unsigned char, AtomicString> namedCollectionKey(CollectionType type, const AtomicString& name) |
| 219 | { |
| 220 | return std::pair<unsigned char, AtomicString>(type, name); |
| 221 | } |
| 222 | |
| 223 | template <class NodeListType> |
| 224 | std::pair<unsigned char, AtomicString> namedNodeListKey(const AtomicString& name) |
| 225 | { |
| 226 | return std::pair<unsigned char, AtomicString>(NodeListTypeIdentifier<NodeListType>::value(), name); |
| 227 | } |
| 228 | |
| 229 | bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&); |
| 230 | |
| 231 | // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway. |
| 232 | ChildNodeList* m_childNodeList; |
| 233 | EmptyNodeList* m_emptyChildNodeList; |
| 234 | |
| 235 | NodeListAtomicNameCacheMap m_atomicNameCaches; |
| 236 | TagCollectionNSCache m_tagCollectionNSCache; |
| 237 | CollectionCacheMap m_cachedCollections; |
| 238 | }; |
| 239 | |
| 240 | class NodeMutationObserverData { |
| 241 | WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED; |
| 242 | public: |
| 243 | Vector<std::unique_ptr<MutationObserverRegistration>> registry; |
| 244 | HashSet<MutationObserverRegistration*> transientRegistry; |
| 245 | |
| 246 | NodeMutationObserverData() { } |
| 247 | }; |
| 248 | |
| 249 | class NodeRareData : public NodeRareDataBase { |
| 250 | WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED; |
| 251 | public: |
| 252 | #if defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS |
| 253 | enum class UseType : uint16_t { |
| 254 | ConnectedFrameCount = 1 << 0, |
| 255 | NodeList = 1 << 1, |
| 256 | MutationObserver = 1 << 2, |
| 257 | |
| 258 | TabIndex = 1 << 3, |
| 259 | StyleFlags = 1 << 4, |
| 260 | MinimumSize = 1 << 5, |
| 261 | ScrollingPosition = 1 << 6, |
| 262 | ComputedStyle = 1 << 7, |
| 263 | Dataset = 1 << 8, |
| 264 | ClassList = 1 << 9, |
| 265 | ShadowRoot = 1 << 10, |
| 266 | CustomElementQueue = 1 << 11, |
| 267 | AttributeMap = 1 << 12, |
| 268 | InteractionObserver = 1 << 13, |
| 269 | PseudoElements = 1 << 14, |
| 270 | }; |
| 271 | #endif |
| 272 | |
| 273 | NodeRareData(RenderObject* renderer) |
| 274 | : NodeRareDataBase(renderer) |
| 275 | , m_connectedFrameCount(0) |
| 276 | { } |
| 277 | |
| 278 | void clearNodeLists() { m_nodeLists = nullptr; } |
| 279 | NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } |
| 280 | NodeListsNodeData& ensureNodeLists() |
| 281 | { |
| 282 | if (!m_nodeLists) |
| 283 | m_nodeLists = std::make_unique<NodeListsNodeData>(); |
| 284 | return *m_nodeLists; |
| 285 | } |
| 286 | |
| 287 | NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); } |
| 288 | NodeMutationObserverData& ensureMutationObserverData() |
| 289 | { |
| 290 | if (!m_mutationObserverData) |
| 291 | m_mutationObserverData = std::make_unique<NodeMutationObserverData>(); |
| 292 | return *m_mutationObserverData; |
| 293 | } |
| 294 | |
| 295 | unsigned connectedSubframeCount() const { return m_connectedFrameCount; } |
| 296 | void incrementConnectedSubframeCount(unsigned amount) |
| 297 | { |
| 298 | m_connectedFrameCount += amount; |
| 299 | } |
| 300 | void decrementConnectedSubframeCount(unsigned amount) |
| 301 | { |
| 302 | ASSERT(m_connectedFrameCount); |
| 303 | ASSERT(amount <= m_connectedFrameCount); |
| 304 | m_connectedFrameCount -= amount; |
| 305 | } |
| 306 | |
| 307 | #if DUMP_NODE_STATISTICS |
| 308 | OptionSet<UseType> useTypes() const |
| 309 | { |
| 310 | OptionSet<UseType> result; |
| 311 | if (m_connectedFrameCount) |
| 312 | result.add(UseType::ConnectedFrameCount); |
| 313 | if (m_nodeLists) |
| 314 | result.add(UseType::NodeList); |
| 315 | if (m_mutationObserverData) |
| 316 | result.add(UseType::MutationObserver); |
| 317 | return result; |
| 318 | } |
| 319 | #endif |
| 320 | |
| 321 | private: |
| 322 | unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames. |
| 323 | |
| 324 | std::unique_ptr<NodeListsNodeData> m_nodeLists; |
| 325 | std::unique_ptr<NodeMutationObserverData> m_mutationObserverData; |
| 326 | }; |
| 327 | |
| 328 | inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode) |
| 329 | { |
| 330 | ASSERT(ownerNode.nodeLists() == this); |
| 331 | if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() |
| 332 | + m_tagCollectionNSCache.size() + m_cachedCollections.size() != 1) |
| 333 | return false; |
| 334 | ownerNode.clearNodeLists(); |
| 335 | return true; |
| 336 | } |
| 337 | |
| 338 | inline NodeRareData* Node::rareData() const |
| 339 | { |
| 340 | ASSERT_WITH_SECURITY_IMPLICATION(hasRareData()); |
| 341 | return static_cast<NodeRareData*>(m_data.m_rareData); |
| 342 | } |
| 343 | |
| 344 | inline NodeRareData& Node::ensureRareData() |
| 345 | { |
| 346 | if (!hasRareData()) |
| 347 | materializeRareData(); |
| 348 | return *rareData(); |
| 349 | } |
| 350 | |
| 351 | } // namespace WebCore |
| 352 | |