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