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 THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NODE_LISTS_NODE_DATA_H_
23 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NODE_LISTS_NODE_DATA_H_
24 
25 #include "base/macros.h"
26 #include "third_party/blink/renderer/core/dom/child_node_list.h"
27 #include "third_party/blink/renderer/core/dom/empty_node_list.h"
28 #include "third_party/blink/renderer/core/dom/qualified_name.h"
29 #include "third_party/blink/renderer/core/dom/tag_collection.h"
30 #include "third_party/blink/renderer/core/html/collection_type.h"
31 #include "third_party/blink/renderer/platform/heap/handle.h"
32 #include "third_party/blink/renderer/platform/heap/heap.h"
33 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
34 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
35 
36 namespace blink {
37 
38 class NodeListsNodeData final : public GarbageCollected<NodeListsNodeData> {
39  public:
GetChildNodeList(ContainerNode & node)40   ChildNodeList* GetChildNodeList(ContainerNode& node) {
41     DCHECK(!child_node_list_ || node == child_node_list_->VirtualOwnerNode());
42     return To<ChildNodeList>(child_node_list_.Get());
43   }
44 
EnsureChildNodeList(ContainerNode & node)45   ChildNodeList* EnsureChildNodeList(ContainerNode& node) {
46     if (child_node_list_)
47       return To<ChildNodeList>(child_node_list_.Get());
48     auto* list = MakeGarbageCollected<ChildNodeList>(node);
49     child_node_list_ = list;
50     return list;
51   }
52 
EnsureEmptyChildNodeList(Node & node)53   EmptyNodeList* EnsureEmptyChildNodeList(Node& node) {
54     if (child_node_list_)
55       return To<EmptyNodeList>(child_node_list_.Get());
56     auto* list = MakeGarbageCollected<EmptyNodeList>(node);
57     child_node_list_ = list;
58     return list;
59   }
60 
61   using NamedNodeListKey = std::pair<CollectionType, AtomicString>;
62   struct NodeListAtomicCacheMapEntryHash {
63     STATIC_ONLY(NodeListAtomicCacheMapEntryHash);
GetHashNodeListAtomicCacheMapEntryHash64     static unsigned GetHash(const NamedNodeListKey& entry) {
65       return DefaultHash<AtomicString>::Hash::GetHash(
66                  entry.second == CSSSelector::UniversalSelectorAtom()
67                      ? g_star_atom
68                      : entry.second) +
69              entry.first;
70     }
EqualNodeListAtomicCacheMapEntryHash71     static bool Equal(const NamedNodeListKey& a, const NamedNodeListKey& b) {
72       return a == b;
73     }
74     static const bool safe_to_compare_to_empty_or_deleted =
75         DefaultHash<AtomicString>::Hash::safe_to_compare_to_empty_or_deleted;
76   };
77 
78   typedef HeapHashMap<NamedNodeListKey,
79                       Member<LiveNodeListBase>,
80                       NodeListAtomicCacheMapEntryHash>
81       NodeListAtomicNameCacheMap;
82   typedef HeapHashMap<QualifiedName, Member<TagCollectionNS>>
83       TagCollectionNSCache;
84 
85   template <typename T>
AddCache(ContainerNode & node,CollectionType collection_type,const AtomicString & name)86   T* AddCache(ContainerNode& node,
87               CollectionType collection_type,
88               const AtomicString& name) {
89     NodeListAtomicNameCacheMap::AddResult result = atomic_name_caches_.insert(
90         std::make_pair(collection_type, name), nullptr);
91     if (!result.is_new_entry) {
92       return static_cast<T*>(result.stored_value->value.Get());
93     }
94 
95     auto* list = MakeGarbageCollected<T>(node, collection_type, name);
96     result.stored_value->value = list;
97     return list;
98   }
99 
100   template <typename T>
AddCache(ContainerNode & node,CollectionType collection_type)101   T* AddCache(ContainerNode& node, CollectionType collection_type) {
102     NodeListAtomicNameCacheMap::AddResult result = atomic_name_caches_.insert(
103         NamedNodeListKey(collection_type, CSSSelector::UniversalSelectorAtom()),
104         nullptr);
105     if (!result.is_new_entry) {
106       return static_cast<T*>(result.stored_value->value.Get());
107     }
108 
109     auto* list = MakeGarbageCollected<T>(node, collection_type);
110     result.stored_value->value = list;
111     return list;
112   }
113 
114   template <typename T>
Cached(CollectionType collection_type)115   T* Cached(CollectionType collection_type) {
116     return static_cast<T*>(atomic_name_caches_.at(NamedNodeListKey(
117         collection_type, CSSSelector::UniversalSelectorAtom())));
118   }
119 
AddCache(ContainerNode & node,const AtomicString & namespace_uri,const AtomicString & local_name)120   TagCollectionNS* AddCache(ContainerNode& node,
121                             const AtomicString& namespace_uri,
122                             const AtomicString& local_name) {
123     QualifiedName name(g_null_atom, local_name, namespace_uri);
124     TagCollectionNSCache::AddResult result =
125         tag_collection_ns_caches_.insert(name, nullptr);
126     if (!result.is_new_entry)
127       return result.stored_value->value;
128 
129     auto* list = MakeGarbageCollected<TagCollectionNS>(
130         node, kTagCollectionNSType, namespace_uri, local_name);
131     result.stored_value->value = list;
132     return list;
133   }
134 
NodeListsNodeData()135   NodeListsNodeData() : child_node_list_(nullptr) {}
136 
137   void InvalidateCaches(const QualifiedName* attr_name = nullptr);
138 
IsEmpty()139   bool IsEmpty() const {
140     return !child_node_list_ && atomic_name_caches_.IsEmpty() &&
141            tag_collection_ns_caches_.IsEmpty();
142   }
143 
AdoptTreeScope()144   void AdoptTreeScope() { InvalidateCaches(); }
145 
AdoptDocument(Document & old_document,Document & new_document)146   void AdoptDocument(Document& old_document, Document& new_document) {
147     DCHECK_NE(old_document, new_document);
148 
149     NodeListAtomicNameCacheMap::const_iterator atomic_name_cache_end =
150         atomic_name_caches_.end();
151     for (NodeListAtomicNameCacheMap::const_iterator it =
152              atomic_name_caches_.begin();
153          it != atomic_name_cache_end; ++it) {
154       LiveNodeListBase* list = it->value;
155       list->DidMoveToDocument(old_document, new_document);
156     }
157 
158     TagCollectionNSCache::const_iterator tag_end =
159         tag_collection_ns_caches_.end();
160     for (TagCollectionNSCache::const_iterator it =
161              tag_collection_ns_caches_.begin();
162          it != tag_end; ++it) {
163       LiveNodeListBase* list = it->value;
164       DCHECK(!list->IsRootedAtTreeScope());
165       list->DidMoveToDocument(old_document, new_document);
166     }
167   }
168 
169   void Trace(Visitor*);
170 
171  private:
172   // Can be a ChildNodeList or an EmptyNodeList.
173   Member<NodeList> child_node_list_;
174   NodeListAtomicNameCacheMap atomic_name_caches_;
175   TagCollectionNSCache tag_collection_ns_caches_;
176   DISALLOW_COPY_AND_ASSIGN(NodeListsNodeData);
177 };
178 
179 template <typename Collection>
EnsureCachedCollection(CollectionType type)180 inline Collection* ContainerNode::EnsureCachedCollection(CollectionType type) {
181   return EnsureNodeLists().AddCache<Collection>(*this, type);
182 }
183 
184 template <typename Collection>
EnsureCachedCollection(CollectionType type,const AtomicString & name)185 inline Collection* ContainerNode::EnsureCachedCollection(
186     CollectionType type,
187     const AtomicString& name) {
188   return EnsureNodeLists().AddCache<Collection>(*this, type, name);
189 }
190 
191 template <typename Collection>
EnsureCachedCollection(CollectionType type,const AtomicString & namespace_uri,const AtomicString & local_name)192 inline Collection* ContainerNode::EnsureCachedCollection(
193     CollectionType type,
194     const AtomicString& namespace_uri,
195     const AtomicString& local_name) {
196   DCHECK_EQ(type, kTagCollectionNSType);
197   return EnsureNodeLists().AddCache(*this, namespace_uri, local_name);
198 }
199 
200 template <typename Collection>
CachedCollection(CollectionType type)201 inline Collection* ContainerNode::CachedCollection(CollectionType type) {
202   NodeListsNodeData* node_lists = NodeLists();
203   return node_lists ? node_lists->Cached<Collection>(type) : nullptr;
204 }
205 
206 }  // namespace blink
207 
208 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NODE_LISTS_NODE_DATA_H_
209