NodeLists should not invalidate on irreleavnt attribute changes
[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     DynamicNodeListCacheBase(NodeListRootType rootType, NodeListInvalidationType invalidationType, CollectionType collectionType = InvalidCollectionType)
47         : m_cachedItem(0)
48         , m_isLengthCacheValid(false)
49         , m_isItemCacheValid(false)
50         , m_rootedAtDocument(rootType == NodeListIsRootedAtDocument)
51         , m_invalidationType(invalidationType)
52         , m_isNameCacheValid(false)
53         , m_collectionType(collectionType)
54     {
55         ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType));
56         ASSERT(m_collectionType == static_cast<unsigned>(collectionType));
57     }
58
59 public:
60     ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootedAtDocument; }
61     ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
62     ALWAYS_INLINE CollectionType type() const { return static_cast<CollectionType>(m_collectionType); }
63
64     void invalidateCache() const;
65
66     static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&);
67
68 protected:
69     ALWAYS_INLINE bool isItemCacheValid() const { return m_isItemCacheValid; }
70     ALWAYS_INLINE Node* cachedItem() const { return m_cachedItem; }
71     ALWAYS_INLINE unsigned cachedItemOffset() const { return m_cachedItemOffset; }
72
73     ALWAYS_INLINE bool isLengthCacheValid() const { return m_isLengthCacheValid; }
74     ALWAYS_INLINE unsigned cachedLength() const { return m_cachedLength; }
75     ALWAYS_INLINE void setLengthCache(unsigned length) const
76     {
77         m_cachedLength = length;
78         m_isLengthCacheValid = true;
79     }
80     ALWAYS_INLINE void setItemCache(Node* item, unsigned offset) const
81     {
82         // FIXME: Assert that item is not null once we've updated DynamicNodeList.
83         m_cachedItem = item;
84         m_cachedItemOffset = offset;
85         m_isItemCacheValid = true;
86     }
87
88     bool hasNameCache() const { return m_isNameCacheValid; }
89     void setHasNameCache() const { m_isNameCacheValid = true; }
90
91 private:
92     mutable Node* m_cachedItem;
93     mutable unsigned m_cachedLength;
94     mutable unsigned m_cachedItemOffset;
95     mutable unsigned m_isLengthCacheValid : 1;
96     mutable unsigned m_isItemCacheValid : 1;
97     const unsigned m_rootedAtDocument : 1;
98     const unsigned m_invalidationType : 4;
99
100     // From HTMLCollection
101     mutable unsigned m_isNameCacheValid : 1;
102     const unsigned m_collectionType : 5;
103 };
104
105 ALWAYS_INLINE bool DynamicNodeListCacheBase::shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName)
106 {
107     switch (type) {
108     case InvalidateOnClassAttrChange:
109         return attrName == HTMLNames::classAttr;
110     case InvalidateOnNameAttrChange:
111         return attrName == HTMLNames::nameAttr;
112     case InvalidateOnIdNameAttrChange:
113         return attrName == HTMLNames::idAttr || attrName == HTMLNames::nameAttr;
114     case InvalidateOnForAttrChange:
115         return attrName == HTMLNames::forAttr;
116     case InvalidateForFormControls:
117         return attrName == HTMLNames::nameAttr || attrName == HTMLNames::idAttr || attrName == HTMLNames::forAttr || attrName == HTMLNames::typeAttr;
118     case InvalidateOnHRefAttrChange:
119         return attrName == HTMLNames::hrefAttr;
120     case InvalidateOnItemAttrChange:
121 #if ENABLE(MICRODATA)
122         return attrName == HTMLNames::itemscopeAttr || attrName == HTMLNames::itempropAttr || attrName == HTMLNames::itemtypeAttr;
123 #endif // Intentionally fall through
124     case DoNotInvalidateOnAttributeChanges:
125         ASSERT_NOT_REACHED();
126         return false;
127     case InvalidateOnAnyAttrChange:
128         return true;
129     }
130     return false;
131 }
132
133 class DynamicNodeList : public NodeList, public DynamicNodeListCacheBase {
134 public:
135     enum NodeListType {
136         ChildNodeListType,
137         ClassNodeListType,
138         NameNodeListType,
139         TagNodeListType,
140         RadioNodeListType,
141         LabelsNodeListType,
142         MicroDataItemListType,
143     };
144     DynamicNodeList(PassRefPtr<Node> ownerNode, NodeListRootType rootType, NodeListInvalidationType invalidationType)
145         : DynamicNodeListCacheBase(rootType, invalidationType)
146         , m_ownerNode(ownerNode)
147     { }
148     virtual ~DynamicNodeList() { }
149
150     // DOM methods & attributes for NodeList
151     virtual unsigned length() const = 0;
152     virtual Node* item(unsigned index) const = 0;
153     virtual Node* itemWithName(const AtomicString&) const;
154
155     // Other methods (not part of DOM)
156     Node* ownerNode() const { return m_ownerNode.get(); }
157
158 protected:
159     Node* rootNode() const
160     {
161         if (isRootedAtDocument() && m_ownerNode->inDocument())
162             return m_ownerNode->document();
163         return m_ownerNode.get();
164     }
165     Document* document() const { return m_ownerNode->document(); }
166     virtual bool nodeMatches(Element*) const = 0;
167
168 private:
169     virtual bool isDynamicNodeList() const OVERRIDE { return true; }
170     RefPtr<Node> m_ownerNode;
171 };
172
173 class DynamicSubtreeNodeList : public DynamicNodeList {
174 public:
175     virtual ~DynamicSubtreeNodeList()
176     {
177         document()->unregisterNodeListCache(this);
178     }
179     virtual unsigned length() const OVERRIDE;
180     virtual Node* item(unsigned index) const OVERRIDE;
181
182 protected:
183     DynamicSubtreeNodeList(PassRefPtr<Node> node, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode)
184         : DynamicNodeList(node, rootType, invalidationType)
185     {
186         document()->registerNodeListCache(this);
187     }
188
189 private:
190     Node* itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
191     Node* itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
192 };
193
194 } // namespace WebCore
195
196 #endif // DynamicNodeList_h