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