Unify HTMLCollection and DynamicNodeList
[WebKit-https.git] / Source / WebCore / dom / DynamicNodeList.h
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #ifndef DynamicNodeList_h
25 #define DynamicNodeList_h
26
27 #include "CollectionType.h"
28 #include "Document.h"
29 #include "HTMLNames.h"
30 #include "NodeList.h"
31 #include <wtf/Forward.h>
32 #include <wtf/RefPtr.h>
33
34 namespace WebCore {
35
36 class Element;
37 class Node;
38
39 enum NodeListRootType {
40     NodeListIsRootedAtNode,
41     NodeListIsRootedAtDocument,
42 };
43
44 class DynamicNodeListCacheBase {
45 public:
46     enum ItemAfterOverrideType {
47         OverridesItemAfter,
48         DoesNotOverrideItemAfter,
49     };
50
51     DynamicNodeListCacheBase(Node* ownerNode, NodeListRootType rootType, NodeListInvalidationType invalidationType,
52         bool shouldOnlyIncludeDirectChildren, CollectionType collectionType, ItemAfterOverrideType itemAfterOverrideType)
53         : m_ownerNode(ownerNode)
54         , m_cachedItem(0)
55         , m_isLengthCacheValid(false)
56         , m_isItemCacheValid(false)
57         , m_rootedAtDocument(rootType == NodeListIsRootedAtDocument)
58         , m_invalidationType(invalidationType)
59         , m_shouldOnlyIncludeDirectChildren(shouldOnlyIncludeDirectChildren)
60         , m_isNameCacheValid(false)
61         , m_collectionType(collectionType)
62         , m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter)
63     {
64         ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType));
65         ASSERT(m_collectionType == static_cast<unsigned>(collectionType));
66         ASSERT(!m_overridesItemAfter || m_collectionType != NodeListCollectionType);
67     }
68
69 public:
70     ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootedAtDocument; }
71     ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
72     ALWAYS_INLINE CollectionType type() const { return static_cast<CollectionType>(m_collectionType); }
73     Node* ownerNode() const { return m_ownerNode.get(); }
74     ALWAYS_INLINE void invalidateCache(const QualifiedName* attrName) const
75     {
76         if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
77             invalidateCache();
78     }
79     void invalidateCache() const;
80
81     static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&);
82
83 protected:
84     Document* document() const { return m_ownerNode->document(); }
85     Node* rootNode() const
86     {
87         if (isRootedAtDocument() && m_ownerNode->inDocument())
88             return m_ownerNode->document();
89         return m_ownerNode.get();
90     }
91     bool overridesItemAfter() const { return m_overridesItemAfter; }
92
93     ALWAYS_INLINE bool isItemCacheValid() const { return m_isItemCacheValid; }
94     ALWAYS_INLINE Node* cachedItem() const { return m_cachedItem; }
95     ALWAYS_INLINE unsigned cachedItemOffset() const { return m_cachedItemOffset; }
96     unsigned cachedElementsArrayOffset() const;
97
98     ALWAYS_INLINE bool isLengthCacheValid() const { return m_isLengthCacheValid; }
99     ALWAYS_INLINE unsigned cachedLength() const { return m_cachedLength; }
100     ALWAYS_INLINE void setLengthCache(unsigned length) const
101     {
102         m_cachedLength = length;
103         m_isLengthCacheValid = true;
104     }
105     ALWAYS_INLINE void setItemCache(Node* item, unsigned offset) const
106     {
107         ASSERT(item);
108         m_cachedItem = item;
109         m_cachedItemOffset = offset;
110         m_isItemCacheValid = true;
111     }
112     void setItemCache(Node* item, unsigned offset, unsigned elementsArrayOffset) const;
113
114     bool hasNameCache() const { return m_isNameCacheValid; }
115     void setHasNameCache() const { m_isNameCacheValid = true; }
116
117     unsigned lengthCommon() const;
118     Node* itemCommon(unsigned offset) const;
119     Node* itemBeforeOrAfterCachedItem(unsigned offset) const;
120     Node* itemAfter(unsigned&, Node* previousItem) const;
121
122 private:
123     bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
124     bool isLastItemCloserThanLastOrCachedItem(unsigned offset) const;
125     bool isFirstItemCloserThanCachedItem(unsigned offset) const;
126     template <bool forward> Node* iterateForNextNode(Node* current) const;
127     template<bool forward> Node* itemBeforeOrAfter(Node* previousItem) const;    
128     Node* itemBefore(Node* previousItem) const;
129
130     RefPtr<Node> m_ownerNode;
131     mutable Node* m_cachedItem;
132     mutable unsigned m_cachedLength;
133     mutable unsigned m_cachedItemOffset;
134     mutable unsigned m_isLengthCacheValid : 1;
135     mutable unsigned m_isItemCacheValid : 1;
136     const unsigned m_rootedAtDocument : 1;
137     const unsigned m_invalidationType : 4;
138     const unsigned m_shouldOnlyIncludeDirectChildren : 1;
139
140     // From HTMLCollection
141     mutable unsigned m_isNameCacheValid : 1;
142     const unsigned m_collectionType : 5;
143     const unsigned m_overridesItemAfter : 1;
144 };
145
146 ALWAYS_INLINE bool DynamicNodeListCacheBase::shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName)
147 {
148     switch (type) {
149     case InvalidateOnClassAttrChange:
150         return attrName == HTMLNames::classAttr;
151     case InvalidateOnNameAttrChange:
152         return attrName == HTMLNames::nameAttr;
153     case InvalidateOnIdNameAttrChange:
154         return attrName == HTMLNames::idAttr || attrName == HTMLNames::nameAttr;
155     case InvalidateOnForAttrChange:
156         return attrName == HTMLNames::forAttr;
157     case InvalidateForFormControls:
158         return attrName == HTMLNames::nameAttr || attrName == HTMLNames::idAttr || attrName == HTMLNames::forAttr
159             || attrName == HTMLNames::formAttr || attrName == HTMLNames::typeAttr;
160     case InvalidateOnHRefAttrChange:
161         return attrName == HTMLNames::hrefAttr;
162     case InvalidateOnItemAttrChange:
163 #if ENABLE(MICRODATA)
164         return attrName == HTMLNames::itemscopeAttr || attrName == HTMLNames::itempropAttr || attrName == HTMLNames::itemtypeAttr;
165 #endif // Intentionally fall through
166     case DoNotInvalidateOnAttributeChanges:
167         return false;
168     case InvalidateOnAnyAttrChange:
169         return true;
170     }
171     return false;
172 }
173
174 class DynamicNodeList : public NodeList, public DynamicNodeListCacheBase {
175 public:
176     enum NodeListType {
177         ChildNodeListType,
178         ClassNodeListType,
179         NameNodeListType,
180         TagNodeListType,
181         RadioNodeListType,
182         LabelsNodeListType,
183         MicroDataItemListType,
184     };
185     DynamicNodeList(PassRefPtr<Node> ownerNode, NodeListType type, NodeListRootType rootType, NodeListInvalidationType invalidationType)
186         : DynamicNodeListCacheBase(ownerNode.get(), rootType, invalidationType, type == ChildNodeListType, NodeListCollectionType, DoesNotOverrideItemAfter)
187     { }
188     virtual ~DynamicNodeList() { }
189
190     // DOM methods & attributes for NodeList
191     virtual unsigned length() const OVERRIDE;
192     virtual Node* item(unsigned offset) const OVERRIDE;
193     virtual Node* itemWithName(const AtomicString&) const;
194
195     // Other methods (not part of DOM)
196     virtual bool nodeMatches(Element*) const = 0;
197
198 private:
199     virtual bool isDynamicNodeList() const OVERRIDE { return true; }
200 };
201
202 class DynamicSubtreeNodeList : public DynamicNodeList {
203 public:
204     virtual ~DynamicSubtreeNodeList()
205     {
206         document()->unregisterNodeListCache(this);
207     }
208
209 protected:
210     DynamicSubtreeNodeList(PassRefPtr<Node> node, NodeListType type, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode)
211         : DynamicNodeList(node, type, rootType, invalidationType)
212     {
213         document()->registerNodeListCache(this);
214     }
215
216 private:
217     Node* itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
218     Node* itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
219 };
220
221 } // namespace WebCore
222
223 #endif // DynamicNodeList_h