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-2019 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#pragma once
25
26#include "CollectionIndexCache.h"
27#include "CollectionTraversal.h"
28#include "Document.h"
29#include "ElementDescendantIterator.h"
30#include "HTMLNames.h"
31#include "NodeList.h"
32#include <wtf/Forward.h>
33#include <wtf/IsoMalloc.h>
34
35namespace WebCore {
36
37class Element;
38
39static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&);
40
41class LiveNodeList : public NodeList {
42 WTF_MAKE_ISO_ALLOCATED(LiveNodeList);
43public:
44 LiveNodeList(ContainerNode& ownerNode, NodeListInvalidationType);
45 virtual ~LiveNodeList();
46
47 virtual bool elementMatches(Element&) const = 0;
48 virtual bool isRootedAtDocument() const = 0;
49
50 ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
51 ContainerNode& ownerNode() const { return m_ownerNode; }
52 ALWAYS_INLINE void invalidateCacheForAttribute(const QualifiedName& attrName) const
53 {
54 if (shouldInvalidateTypeOnAttributeChange(invalidationType(), attrName))
55 invalidateCache();
56 }
57 virtual void invalidateCacheForDocument(Document&) const = 0;
58 void invalidateCache() const { invalidateCacheForDocument(document()); }
59
60 bool isRegisteredForInvalidationAtDocument() const { return m_isRegisteredForInvalidationAtDocument; }
61 void setRegisteredForInvalidationAtDocument(bool f) { m_isRegisteredForInvalidationAtDocument = f; }
62
63protected:
64 Document& document() const { return m_ownerNode->document(); }
65
66private:
67 bool isLiveNodeList() const final { return true; }
68
69 ContainerNode& rootNode() const;
70
71 Ref<ContainerNode> m_ownerNode;
72
73 const unsigned m_invalidationType;
74 bool m_isRegisteredForInvalidationAtDocument;
75};
76
77template <class NodeListType>
78class CachedLiveNodeList : public LiveNodeList {
79 WTF_MAKE_ISO_NONALLOCATABLE(CachedLiveNodeList);
80public:
81 virtual ~CachedLiveNodeList();
82
83 unsigned length() const final { return m_indexCache.nodeCount(nodeList()); }
84 Element* item(unsigned offset) const override { return m_indexCache.nodeAt(nodeList(), offset); }
85
86 // For CollectionIndexCache
87 ElementDescendantIterator collectionBegin() const { return CollectionTraversal<CollectionTraversalType::Descendants>::begin(nodeList(), rootNode()); }
88 ElementDescendantIterator collectionLast() const { return CollectionTraversal<CollectionTraversalType::Descendants>::last(nodeList(), rootNode()); }
89 ElementDescendantIterator collectionEnd() const { return ElementDescendantIterator(); }
90 void collectionTraverseForward(ElementDescendantIterator& current, unsigned count, unsigned& traversedCount) const { CollectionTraversal<CollectionTraversalType::Descendants>::traverseForward(nodeList(), current, count, traversedCount); }
91 void collectionTraverseBackward(ElementDescendantIterator& current, unsigned count) const { CollectionTraversal<CollectionTraversalType::Descendants>::traverseBackward(nodeList(), current, count); }
92 bool collectionCanTraverseBackward() const { return true; }
93 void willValidateIndexCache() const { document().registerNodeListForInvalidation(const_cast<CachedLiveNodeList<NodeListType>&>(*this)); }
94
95 void invalidateCacheForDocument(Document&) const final;
96 size_t memoryCost() const final
97 {
98 // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful
99 // about what data we access here and how. Accessing m_indexCache is safe because
100 // because it doesn't involve any pointer chasing.
101 return m_indexCache.memoryCost();
102 }
103
104protected:
105 CachedLiveNodeList(ContainerNode& rootNode, NodeListInvalidationType);
106
107private:
108 NodeListType& nodeList() { return static_cast<NodeListType&>(*this); }
109 const NodeListType& nodeList() const { return static_cast<const NodeListType&>(*this); }
110
111 ContainerNode& rootNode() const;
112
113 mutable CollectionIndexCache<NodeListType, ElementDescendantIterator> m_indexCache;
114};
115
116ALWAYS_INLINE bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName)
117{
118 switch (type) {
119 case InvalidateOnClassAttrChange:
120 return attrName == HTMLNames::classAttr;
121 case InvalidateOnNameAttrChange:
122 return attrName == HTMLNames::nameAttr;
123 case InvalidateOnIdNameAttrChange:
124 return attrName == HTMLNames::idAttr || attrName == HTMLNames::nameAttr;
125 case InvalidateOnForTypeAttrChange:
126 return attrName == HTMLNames::forAttr || attrName == HTMLNames::typeAttr;
127 case InvalidateForFormControls:
128 return attrName == HTMLNames::nameAttr || attrName == HTMLNames::idAttr || attrName == HTMLNames::forAttr
129 || attrName == HTMLNames::formAttr || attrName == HTMLNames::typeAttr;
130 case InvalidateOnHRefAttrChange:
131 return attrName == HTMLNames::hrefAttr;
132 case DoNotInvalidateOnAttributeChanges:
133 return false;
134 case InvalidateOnAnyAttrChange:
135 return true;
136 }
137 return false;
138}
139
140template <class NodeListType>
141CachedLiveNodeList<NodeListType>::CachedLiveNodeList(ContainerNode& ownerNode, NodeListInvalidationType invalidationType)
142 : LiveNodeList(ownerNode, invalidationType)
143 , m_indexCache(nodeList())
144{
145}
146
147template <class NodeListType>
148CachedLiveNodeList<NodeListType>::~CachedLiveNodeList()
149{
150 if (m_indexCache.hasValidCache(nodeList()))
151 document().unregisterNodeListForInvalidation(*this);
152}
153
154template <class NodeListType>
155inline ContainerNode& CachedLiveNodeList<NodeListType>::rootNode() const
156{
157 if (nodeList().isRootedAtDocument() && ownerNode().isConnected())
158 return ownerNode().document();
159
160 return ownerNode();
161}
162
163template <class NodeListType>
164void CachedLiveNodeList<NodeListType>::invalidateCacheForDocument(Document& document) const
165{
166 if (!m_indexCache.hasValidCache(nodeList()))
167 return;
168 document.unregisterNodeListForInvalidation(const_cast<NodeListType&>(nodeList()));
169 m_indexCache.invalidate(nodeList());
170}
171
172} // namespace WebCore
173