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-2015 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 ContainerNode_h
25#define ContainerNode_h
26
27#include "CollectionType.h"
28#include "ExceptionCodePlaceholder.h"
29#include "Node.h"
30
31namespace WebCore {
32
33class HTMLCollection;
34class NodeList;
35class NodeOrString;
36class QualifiedName;
37class RenderElement;
38
39class NoEventDispatchAssertion {
40public:
41 NoEventDispatchAssertion()
42 {
43#if !ASSERT_DISABLED
44 if (!isMainThread())
45 return;
46 ++s_count;
47#endif
48 }
49
50 NoEventDispatchAssertion(const NoEventDispatchAssertion&)
51 : NoEventDispatchAssertion()
52 {
53 }
54
55 ~NoEventDispatchAssertion()
56 {
57#if !ASSERT_DISABLED
58 if (!isMainThread())
59 return;
60 ASSERT(s_count);
61 s_count--;
62#endif
63 }
64
65 static bool isEventDispatchForbidden()
66 {
67#if ASSERT_DISABLED
68 return false;
69#else
70 return isMainThread() && s_count;
71#endif
72 }
73
74#if !ASSERT_DISABLED
75
76private:
77 WEBCORE_EXPORT static unsigned s_count;
78
79#endif
80};
81
82class ContainerNode : public Node {
83public:
84 virtual ~ContainerNode();
85
86 Node* firstChild() const { return m_firstChild; }
87 static ptrdiff_t firstChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_firstChild); }
88 Node* lastChild() const { return m_lastChild; }
89 bool hasChildNodes() const { return m_firstChild; }
90 bool hasOneChild() const { return m_firstChild && !m_firstChild->nextSibling(); }
91
92 bool directChildNeedsStyleRecalc() const { return getFlag(DirectChildNeedsStyleRecalcFlag); }
93 void setDirectChildNeedsStyleRecalc() { setFlag(DirectChildNeedsStyleRecalcFlag); }
94
95 WEBCORE_EXPORT unsigned countChildNodes() const;
96 WEBCORE_EXPORT Node* traverseToChildAt(unsigned) const;
97
98 bool insertBefore(Ref<Node>&& newChild, Node* refChild, ExceptionCode& = ASSERT_NO_EXCEPTION);
99 bool replaceChild(Ref<Node>&& newChild, Node& oldChild, ExceptionCode& = ASSERT_NO_EXCEPTION);
100 WEBCORE_EXPORT bool removeChild(Node& child, ExceptionCode& = ASSERT_NO_EXCEPTION);
101 WEBCORE_EXPORT bool appendChild(Ref<Node>&& newChild, ExceptionCode& = ASSERT_NO_EXCEPTION);
102
103 // These methods are only used during parsing.
104 // They don't send DOM mutation events or handle reparenting.
105 // However, arbitrary code may be run by beforeload handlers.
106 void parserAppendChild(Ref<Node>&&);
107 void parserRemoveChild(Node&);
108 void parserInsertBefore(Ref<Node>&& newChild, Node& refChild);
109
110 void removeChildren();
111 void takeAllChildrenFrom(ContainerNode*);
112
113 void cloneChildNodes(ContainerNode& clone);
114
115 enum ChildChangeType { ElementInserted, ElementRemoved, TextInserted, TextRemoved, TextChanged, AllChildrenRemoved, NonContentsChildChanged };
116 enum ChildChangeSource { ChildChangeSourceParser, ChildChangeSourceAPI };
117 struct ChildChange {
118 ChildChangeType type;
119 Element* previousSiblingElement;
120 Element* nextSiblingElement;
121 ChildChangeSource source;
122 };
123 virtual void childrenChanged(const ChildChange&);
124
125 void disconnectDescendantFrames();
126
127 using Node::setAttributeEventListener;
128 void setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value);
129
130 RenderElement* renderer() const;
131
132 // Return a bounding box in absolute coordinates enclosing this node and all its descendants.
133 // This gives the area within which events may get handled by a hander registered on this node.
134 virtual LayoutRect absoluteEventHandlerBounds(bool& /* includesFixedPositionElements */) { return LayoutRect(); }
135
136 Element* querySelector(const String& selectors, ExceptionCode&);
137 RefPtr<NodeList> querySelectorAll(const String& selectors, ExceptionCode&);
138
139 Ref<HTMLCollection> getElementsByTagName(const AtomicString&);
140 RefPtr<NodeList> getElementsByTagNameForObjC(const AtomicString&);
141 Ref<HTMLCollection> getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName);
142 RefPtr<NodeList> getElementsByTagNameNSForObjC(const AtomicString& namespaceURI, const AtomicString& localName);
143 Ref<NodeList> getElementsByName(const String& elementName);
144 Ref<HTMLCollection> getElementsByClassName(const AtomicString& classNames);
145 Ref<NodeList> getElementsByClassNameForObjC(const AtomicString& classNames);
146 Ref<RadioNodeList> radioNodeList(const AtomicString&);
147
148 // From the ParentNode interface - https://dom.spec.whatwg.org/#interface-parentnode
149 Ref<HTMLCollection> children();
150 Element* firstElementChild() const;
151 Element* lastElementChild() const;
152 unsigned childElementCount() const;
153 void append(Vector<NodeOrString>&&, ExceptionCode&);
154 void prepend(Vector<NodeOrString>&&, ExceptionCode&);
155
156 bool ensurePreInsertionValidity(Node& newChild, Node* refChild, ExceptionCode&);
157
158protected:
159 explicit ContainerNode(Document&, ConstructionType = CreateContainer);
160
161 friend void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode&);
162
163 void removeDetachedChildren();
164 void setFirstChild(Node* child) { m_firstChild = child; }
165 void setLastChild(Node* child) { m_lastChild = child; }
166
167 HTMLCollection* cachedHTMLCollection(CollectionType);
168
169private:
170 void removeBetween(Node* previousChild, Node* nextChild, Node& oldChild);
171 void insertBeforeCommon(Node& nextChild, Node& oldChild);
172 void appendChildCommon(Node&);
173
174 void notifyChildInserted(Node& child, ChildChangeSource);
175 void notifyChildRemoved(Node& child, Node* previousSibling, Node* nextSibling, ChildChangeSource);
176
177 void updateTreeAfterInsertion(Node& child);
178
179 bool isContainerNode() const = delete;
180
181 void willRemoveChild(Node& child);
182
183 Node* m_firstChild { nullptr };
184 Node* m_lastChild { nullptr };
185};
186
187inline ContainerNode::ContainerNode(Document& document, ConstructionType type)
188 : Node(document, type)
189{
190}
191
192inline unsigned Node::countChildNodes() const
193{
194 if (!is<ContainerNode>(*this))
195 return 0;
196 return downcast<ContainerNode>(*this).countChildNodes();
197}
198
199inline Node* Node::traverseToChildAt(unsigned index) const
200{
201 if (!is<ContainerNode>(*this))
202 return nullptr;
203 return downcast<ContainerNode>(*this).traverseToChildAt(index);
204}
205
206inline Node* Node::firstChild() const
207{
208 if (!is<ContainerNode>(*this))
209 return nullptr;
210 return downcast<ContainerNode>(*this).firstChild();
211}
212
213inline Node* Node::lastChild() const
214{
215 if (!is<ContainerNode>(*this))
216 return nullptr;
217 return downcast<ContainerNode>(*this).lastChild();
218}
219
220inline Node* Node::highestAncestor() const
221{
222 Node* node = const_cast<Node*>(this);
223 Node* highest = node;
224 for (; node; node = node->parentNode())
225 highest = node;
226 return highest;
227}
228
229inline bool Node::isTreeScope() const
230{
231 return &treeScope().rootNode() == this;
232}
233
234// This constant controls how much buffer is initially allocated
235// for a Node Vector that is used to store child Nodes of a given Node.
236// FIXME: Optimize the value.
237const int initialNodeVectorSize = 11;
238typedef Vector<Ref<Node>, initialNodeVectorSize> NodeVector;
239
240inline void getChildNodes(Node& node, NodeVector& nodes)
241{
242 ASSERT(nodes.isEmpty());
243 for (Node* child = node.firstChild(); child; child = child->nextSibling())
244 nodes.append(*child);
245}
246
247class ChildNodesLazySnapshot {
248 WTF_MAKE_NONCOPYABLE(ChildNodesLazySnapshot);
249 WTF_MAKE_FAST_ALLOCATED;
250public:
251 explicit ChildNodesLazySnapshot(Node& parentNode)
252 : m_currentNode(parentNode.firstChild())
253 , m_currentIndex(0)
254 , m_hasSnapshot(false)
255 {
256 m_nextSnapshot = latestSnapshot;
257 latestSnapshot = this;
258 }
259
260 ALWAYS_INLINE ~ChildNodesLazySnapshot()
261 {
262 latestSnapshot = m_nextSnapshot;
263 }
264
265 // Returns 0 if there is no next Node.
266 RefPtr<Node> nextNode()
267 {
268 if (LIKELY(!hasSnapshot())) {
269 RefPtr<Node> node = m_currentNode.release();
270 if (node)
271 m_currentNode = node->nextSibling();
272 return node.release();
273 }
274 if (m_currentIndex >= m_snapshot.size())
275 return 0;
276 return m_snapshot[m_currentIndex++];
277 }
278
279 void takeSnapshot()
280 {
281 if (hasSnapshot())
282 return;
283 m_hasSnapshot = true;
284 Node* node = m_currentNode.get();
285 while (node) {
286 m_snapshot.append(node);
287 node = node->nextSibling();
288 }
289 }
290
291 ChildNodesLazySnapshot* nextSnapshot() { return m_nextSnapshot; }
292 bool hasSnapshot() { return m_hasSnapshot; }
293
294 static void takeChildNodesLazySnapshot()
295 {
296 ChildNodesLazySnapshot* snapshot = latestSnapshot;
297 while (snapshot && !snapshot->hasSnapshot()) {
298 snapshot->takeSnapshot();
299 snapshot = snapshot->nextSnapshot();
300 }
301 }
302
303private:
304 static ChildNodesLazySnapshot* latestSnapshot;
305
306 RefPtr<Node> m_currentNode;
307 unsigned m_currentIndex;
308 bool m_hasSnapshot;
309 Vector<RefPtr<Node>> m_snapshot; // Lazily instantiated.
310 ChildNodesLazySnapshot* m_nextSnapshot;
311};
312
313} // namespace WebCore
314
315SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ContainerNode)
316 static bool isType(const WebCore::Node& node) { return node.isContainerNode(); }
317SPECIALIZE_TYPE_TRAITS_END()
318
319#endif // ContainerNode_h
320