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-2018 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 "CollectionType.h"
27#include "Node.h"
28#include <wtf/WeakPtr.h>
29
30namespace WebCore {
31
32class HTMLCollection;
33class RadioNodeList;
34class RenderElement;
35
36const int initialNodeVectorSize = 11; // Covers 99.5%. See webkit.org/b/80706
37typedef Vector<Ref<Node>, initialNodeVectorSize> NodeVector;
38
39class ContainerNode : public CanMakeWeakPtr<ContainerNode>, public Node {
40 WTF_MAKE_ISO_ALLOCATED(ContainerNode);
41public:
42 virtual ~ContainerNode();
43
44 Node* firstChild() const { return m_firstChild; }
45 static ptrdiff_t firstChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_firstChild); }
46 Node* lastChild() const { return m_lastChild; }
47 static ptrdiff_t lastChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_lastChild); }
48 bool hasChildNodes() const { return m_firstChild; }
49 bool hasOneChild() const { return m_firstChild && !m_firstChild->nextSibling(); }
50
51 bool directChildNeedsStyleRecalc() const { return getFlag(DirectChildNeedsStyleRecalcFlag); }
52 void setDirectChildNeedsStyleRecalc() { setFlag(DirectChildNeedsStyleRecalcFlag); }
53
54 WEBCORE_EXPORT unsigned countChildNodes() const;
55 WEBCORE_EXPORT Node* traverseToChildAt(unsigned) const;
56
57 ExceptionOr<void> insertBefore(Node& newChild, Node* refChild);
58 ExceptionOr<void> replaceChild(Node& newChild, Node& oldChild);
59 WEBCORE_EXPORT ExceptionOr<void> removeChild(Node& child);
60 WEBCORE_EXPORT ExceptionOr<void> appendChild(Node& newChild);
61 void replaceAllChildren(Ref<Node>&&);
62 void replaceAllChildren(std::nullptr_t);
63
64 // These methods are only used during parsing.
65 // They don't send DOM mutation events or handle reparenting.
66 // However, arbitrary code may be run by beforeload handlers.
67 void parserAppendChild(Node&);
68 void parserRemoveChild(Node&);
69 void parserInsertBefore(Node& newChild, Node& refChild);
70
71 void removeChildren();
72
73 void takeAllChildrenFrom(ContainerNode*);
74
75 void cloneChildNodes(ContainerNode& clone);
76
77 enum ChildChangeType { ElementInserted, ElementRemoved, TextInserted, TextRemoved, TextChanged, AllChildrenRemoved, NonContentsChildRemoved, NonContentsChildInserted, AllChildrenReplaced };
78 enum class ChildChangeSource { Parser, API };
79 struct ChildChange {
80 ChildChangeType type;
81 Element* previousSiblingElement;
82 Element* nextSiblingElement;
83 ChildChangeSource source;
84
85 bool isInsertion() const
86 {
87 switch (type) {
88 case ElementInserted:
89 case TextInserted:
90 case NonContentsChildInserted:
91 case AllChildrenReplaced:
92 return true;
93 case ElementRemoved:
94 case TextRemoved:
95 case TextChanged:
96 case AllChildrenRemoved:
97 case NonContentsChildRemoved:
98 return false;
99 }
100 ASSERT_NOT_REACHED();
101 return false;
102 }
103 };
104 virtual void childrenChanged(const ChildChange&);
105
106 void disconnectDescendantFrames();
107
108 RenderElement* renderer() const;
109
110 // Return a bounding box in absolute coordinates enclosing this node and all its descendants.
111 // This gives the area within which events may get handled by a hander registered on this node.
112 virtual LayoutRect absoluteEventHandlerBounds(bool& /* includesFixedPositionElements */) { return LayoutRect(); }
113
114 WEBCORE_EXPORT ExceptionOr<Element*> querySelector(const String& selectors);
115 WEBCORE_EXPORT ExceptionOr<Ref<NodeList>> querySelectorAll(const String& selectors);
116
117 WEBCORE_EXPORT Ref<HTMLCollection> getElementsByTagName(const AtomString&);
118 WEBCORE_EXPORT Ref<HTMLCollection> getElementsByTagNameNS(const AtomString& namespaceURI, const AtomString& localName);
119 WEBCORE_EXPORT Ref<NodeList> getElementsByName(const String& elementName);
120 WEBCORE_EXPORT Ref<HTMLCollection> getElementsByClassName(const AtomString& classNames);
121 Ref<RadioNodeList> radioNodeList(const AtomString&);
122
123 // From the ParentNode interface - https://dom.spec.whatwg.org/#interface-parentnode
124 WEBCORE_EXPORT Ref<HTMLCollection> children();
125 WEBCORE_EXPORT Element* firstElementChild() const;
126 WEBCORE_EXPORT Element* lastElementChild() const;
127 WEBCORE_EXPORT unsigned childElementCount() const;
128 ExceptionOr<void> append(Vector<NodeOrString>&&);
129 ExceptionOr<void> prepend(Vector<NodeOrString>&&);
130
131 ExceptionOr<void> ensurePreInsertionValidity(Node& newChild, Node* refChild);
132
133protected:
134 explicit ContainerNode(Document&, ConstructionType = CreateContainer);
135
136 friend void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode&);
137
138 void removeDetachedChildren();
139 void setFirstChild(Node* child) { m_firstChild = child; }
140 void setLastChild(Node* child) { m_lastChild = child; }
141
142 HTMLCollection* cachedHTMLCollection(CollectionType);
143
144private:
145 void executePreparedChildrenRemoval();
146 enum class DeferChildrenChanged { Yes, No };
147 NodeVector removeAllChildrenWithScriptAssertion(ChildChangeSource, DeferChildrenChanged = DeferChildrenChanged::No);
148 bool removeNodeWithScriptAssertion(Node&, ChildChangeSource);
149
150 void removeBetween(Node* previousChild, Node* nextChild, Node& oldChild);
151 ExceptionOr<void> appendChildWithoutPreInsertionValidityCheck(Node&);
152 void insertBeforeCommon(Node& nextChild, Node& oldChild);
153 void appendChildCommon(Node&);
154
155 void rebuildSVGExtensionsElementsIfNecessary();
156
157 bool isContainerNode() const = delete;
158
159 Node* m_firstChild { nullptr };
160 Node* m_lastChild { nullptr };
161};
162
163inline ContainerNode::ContainerNode(Document& document, ConstructionType type)
164 : Node(document, type)
165{
166}
167
168inline unsigned Node::countChildNodes() const
169{
170 if (!is<ContainerNode>(*this))
171 return 0;
172 return downcast<ContainerNode>(*this).countChildNodes();
173}
174
175inline Node* Node::traverseToChildAt(unsigned index) const
176{
177 if (!is<ContainerNode>(*this))
178 return nullptr;
179 return downcast<ContainerNode>(*this).traverseToChildAt(index);
180}
181
182inline Node* Node::firstChild() const
183{
184 if (!is<ContainerNode>(*this))
185 return nullptr;
186 return downcast<ContainerNode>(*this).firstChild();
187}
188
189inline Node* Node::lastChild() const
190{
191 if (!is<ContainerNode>(*this))
192 return nullptr;
193 return downcast<ContainerNode>(*this).lastChild();
194}
195
196inline Node& Node::rootNode() const
197{
198 if (isInTreeScope())
199 return treeScope().rootNode();
200 return traverseToRootNode();
201}
202
203inline NodeVector collectChildNodes(Node& node)
204{
205 NodeVector children;
206 for (Node* child = node.firstChild(); child; child = child->nextSibling())
207 children.append(*child);
208 return children;
209}
210
211class ChildNodesLazySnapshot {
212 WTF_MAKE_NONCOPYABLE(ChildNodesLazySnapshot);
213 WTF_MAKE_FAST_ALLOCATED;
214public:
215 explicit ChildNodesLazySnapshot(Node& parentNode)
216 : m_currentNode(parentNode.firstChild())
217 , m_currentIndex(0)
218 , m_hasSnapshot(false)
219 {
220 m_nextSnapshot = latestSnapshot;
221 latestSnapshot = this;
222 }
223
224 ALWAYS_INLINE ~ChildNodesLazySnapshot()
225 {
226 latestSnapshot = m_nextSnapshot;
227 }
228
229 // Returns 0 if there is no next Node.
230 RefPtr<Node> nextNode()
231 {
232 if (LIKELY(!hasSnapshot())) {
233 RefPtr<Node> node = WTFMove(m_currentNode);
234 if (node)
235 m_currentNode = node->nextSibling();
236 return node;
237 }
238 if (m_currentIndex >= m_snapshot.size())
239 return nullptr;
240 return m_snapshot[m_currentIndex++];
241 }
242
243 void takeSnapshot()
244 {
245 if (hasSnapshot())
246 return;
247 m_hasSnapshot = true;
248 Node* node = m_currentNode.get();
249 while (node) {
250 m_snapshot.append(node);
251 node = node->nextSibling();
252 }
253 }
254
255 ChildNodesLazySnapshot* nextSnapshot() { return m_nextSnapshot; }
256 bool hasSnapshot() { return m_hasSnapshot; }
257
258 static void takeChildNodesLazySnapshot()
259 {
260 ChildNodesLazySnapshot* snapshot = latestSnapshot;
261 while (snapshot && !snapshot->hasSnapshot()) {
262 snapshot->takeSnapshot();
263 snapshot = snapshot->nextSnapshot();
264 }
265 }
266
267private:
268 static ChildNodesLazySnapshot* latestSnapshot;
269
270 RefPtr<Node> m_currentNode;
271 unsigned m_currentIndex;
272 bool m_hasSnapshot;
273 Vector<RefPtr<Node>> m_snapshot; // Lazily instantiated.
274 ChildNodesLazySnapshot* m_nextSnapshot;
275};
276
277} // namespace WebCore
278
279SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ContainerNode)
280 static bool isType(const WebCore::Node& node) { return node.isContainerNode(); }
281SPECIALIZE_TYPE_TRAITS_END()
282