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, 2005, 2006, 2007, 2008, 2009 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 #include "config.h"
24 #include "core/dom/ContainerNode.h"
25
26 #include "bindings/v8/ExceptionState.h"
27 #include "core/dom/ChildListMutationScope.h"
28 #include "core/dom/ContainerNodeAlgorithms.h"
29 #include "core/dom/ElementTraversal.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/dom/FullscreenElementStack.h"
32 #include "core/dom/NodeChildRemovalTracker.h"
33 #include "core/dom/NodeRareData.h"
34 #include "core/dom/NodeRenderStyle.h"
35 #include "core/dom/NodeTraversal.h"
36 #include "core/events/MutationEvent.h"
37 #include "core/events/ThreadLocalEventNames.h"
38 #include "core/html/HTMLCollection.h"
39 #include "core/rendering/InlineTextBox.h"
40 #include "core/rendering/RenderText.h"
41 #include "core/rendering/RenderTheme.h"
42 #include "core/rendering/RenderView.h"
43 #include "core/rendering/RenderWidget.h"
44
45 using namespace std;
46
47 namespace WebCore {
48
49 static void dispatchChildInsertionEvents(Node&);
50 static void dispatchChildRemovalEvents(Node&);
51
52 ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot = 0;
53
54 #ifndef NDEBUG
55 unsigned NoEventDispatchAssertion::s_count = 0;
56 #endif
57
collectChildrenAndRemoveFromOldParent(Node & node,NodeVector & nodes,ExceptionState & exceptionState)58 static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionState& exceptionState)
59 {
60 if (!node.isDocumentFragment()) {
61 nodes.append(&node);
62 if (ContainerNode* oldParent = node.parentNode())
63 oldParent->removeChild(&node, exceptionState);
64 return;
65 }
66 getChildNodes(node, nodes);
67 toContainerNode(node).removeChildren();
68 }
69
removeDetachedChildren()70 void ContainerNode::removeDetachedChildren()
71 {
72 if (connectedSubframeCount()) {
73 for (Node* child = firstChild(); child; child = child->nextSibling())
74 child->updateAncestorConnectedSubframeCountForRemoval();
75 }
76 ASSERT(needsAttach());
77 removeDetachedChildrenInContainer<Node, ContainerNode>(*this);
78 }
79
parserTakeAllChildrenFrom(ContainerNode & oldParent)80 void ContainerNode::parserTakeAllChildrenFrom(ContainerNode& oldParent)
81 {
82 while (RefPtr<Node> child = oldParent.firstChild()) {
83 oldParent.parserRemoveChild(*child);
84 treeScope().adoptIfNeeded(*child);
85 parserAppendChild(child.get());
86 }
87 }
88
~ContainerNode()89 ContainerNode::~ContainerNode()
90 {
91 willBeDeletedFromDocument();
92 removeDetachedChildren();
93 }
94
isChildTypeAllowed(const Node & child) const95 bool ContainerNode::isChildTypeAllowed(const Node& child) const
96 {
97 if (!child.isDocumentFragment())
98 return childTypeAllowed(child.nodeType());
99
100 for (Node* node = child.firstChild(); node; node = node->nextSibling()) {
101 if (!childTypeAllowed(node->nodeType()))
102 return false;
103 }
104 return true;
105 }
106
containsConsideringHostElements(const Node & newChild) const107 bool ContainerNode::containsConsideringHostElements(const Node& newChild) const
108 {
109 if (isInShadowTree() || document() == document().templateDocument())
110 return newChild.containsIncludingHostElements(*this);
111 return newChild.contains(this);
112 }
113
checkAcceptChild(const Node * newChild,const Node * oldChild,ExceptionState & exceptionState) const114 bool ContainerNode::checkAcceptChild(const Node* newChild, const Node* oldChild, ExceptionState& exceptionState) const
115 {
116 // Not mentioned in spec: throw NotFoundError if newChild is null
117 if (!newChild) {
118 exceptionState.throwDOMException(NotFoundError, "The new child element is null.");
119 return false;
120 }
121
122 // Use common case fast path if possible.
123 if ((newChild->isElementNode() || newChild->isTextNode()) && isElementNode()) {
124 ASSERT(isChildTypeAllowed(*newChild));
125 if (containsConsideringHostElements(*newChild)) {
126 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
127 return false;
128 }
129 return true;
130 }
131
132 // This should never happen, but also protect release builds from tree corruption.
133 ASSERT(!newChild->isPseudoElement());
134 if (newChild->isPseudoElement()) {
135 exceptionState.throwDOMException(HierarchyRequestError, "The new child element is a pseudo-element.");
136 return false;
137 }
138
139 if (containsConsideringHostElements(*newChild)) {
140 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
141 return false;
142 }
143
144 if (oldChild && isDocumentNode()) {
145 if (!toDocument(this)->canReplaceChild(*newChild, *oldChild)) {
146 // FIXME: Adjust 'Document::canReplaceChild' to return some additional detail (or an error message).
147 exceptionState.throwDOMException(HierarchyRequestError, "Failed to replace child.");
148 return false;
149 }
150 } else if (!isChildTypeAllowed(*newChild)) {
151 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type '" + newChild->nodeName() + "' may not be inserted inside nodes of type '" + nodeName() + "'.");
152 return false;
153 }
154
155 return true;
156 }
157
checkAcceptChildGuaranteedNodeTypes(const Node & newChild,ExceptionState & exceptionState) const158 bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node& newChild, ExceptionState& exceptionState) const
159 {
160 ASSERT(isChildTypeAllowed(newChild));
161 if (newChild.contains(this)) {
162 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
163 return false;
164 }
165 return true;
166 }
167
insertBefore(PassRefPtr<Node> newChild,Node * refChild,ExceptionState & exceptionState)168 void ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionState& exceptionState)
169 {
170 // Check that this node is not "floating".
171 // If it is, it can be deleted as a side effect of sending mutation events.
172 ASSERT(refCount() || parentOrShadowHostNode());
173
174 RefPtr<Node> protect(this);
175
176 // insertBefore(node, 0) is equivalent to appendChild(node)
177 if (!refChild) {
178 appendChild(newChild, exceptionState);
179 return;
180 }
181
182 // Make sure adding the new child is OK.
183 if (!checkAcceptChild(newChild.get(), 0, exceptionState))
184 return;
185 ASSERT(newChild);
186
187 // NotFoundError: Raised if refChild is not a child of this node
188 if (refChild->parentNode() != this) {
189 exceptionState.throwDOMException(NotFoundError, "The node before which the new node is to be inserted is not a child of this node.");
190 return;
191 }
192
193 if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do
194 return;
195
196 RefPtr<Node> next = refChild;
197
198 NodeVector targets;
199 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
200 if (exceptionState.hadException())
201 return;
202 if (targets.isEmpty())
203 return;
204
205 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
206 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState))
207 return;
208
209 InspectorInstrumentation::willInsertDOMNode(this);
210
211 ChildListMutationScope mutation(*this);
212 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
213 ASSERT(*it);
214 Node& child = **it;
215
216 // Due to arbitrary code running in response to a DOM mutation event it's
217 // possible that "next" is no longer a child of "this".
218 // It's also possible that "child" has been inserted elsewhere.
219 // In either of those cases, we'll just stop.
220 if (next->parentNode() != this)
221 break;
222 if (child.parentNode())
223 break;
224
225 treeScope().adoptIfNeeded(child);
226
227 insertBeforeCommon(*next, child);
228
229 updateTreeAfterInsertion(child);
230 }
231
232 dispatchSubtreeModifiedEvent();
233 }
234
insertBeforeCommon(Node & nextChild,Node & newChild)235 void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
236 {
237 NoEventDispatchAssertion assertNoEventDispatch;
238
239 ASSERT(!newChild.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
240 ASSERT(!newChild.nextSibling());
241 ASSERT(!newChild.previousSibling());
242 ASSERT(!newChild.isShadowRoot());
243
244 Node* prev = nextChild.previousSibling();
245 ASSERT(m_lastChild != prev);
246 nextChild.setPreviousSibling(&newChild);
247 if (prev) {
248 ASSERT(m_firstChild != nextChild);
249 ASSERT(prev->nextSibling() == nextChild);
250 prev->setNextSibling(&newChild);
251 } else {
252 ASSERT(m_firstChild == nextChild);
253 m_firstChild = &newChild;
254 }
255 newChild.setParentOrShadowHostNode(this);
256 newChild.setPreviousSibling(prev);
257 newChild.setNextSibling(&nextChild);
258 }
259
parserInsertBefore(PassRefPtr<Node> newChild,Node & nextChild)260 void ContainerNode::parserInsertBefore(PassRefPtr<Node> newChild, Node& nextChild)
261 {
262 ASSERT(newChild);
263 ASSERT(nextChild.parentNode() == this);
264 ASSERT(!newChild->isDocumentFragment());
265 ASSERT(!hasTagName(HTMLNames::templateTag));
266
267 if (nextChild.previousSibling() == newChild || nextChild == newChild) // nothing to do
268 return;
269
270 if (document() != newChild->document())
271 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
272
273 insertBeforeCommon(nextChild, *newChild);
274
275 newChild->updateAncestorConnectedSubframeCountForInsertion();
276
277 ChildListMutationScope(*this).childAdded(*newChild);
278
279 childrenChanged(true, newChild->previousSibling(), &nextChild, 1);
280
281 ChildNodeInsertionNotifier(*this).notify(*newChild);
282 }
283
replaceChild(PassRefPtr<Node> newChild,Node * oldChild,ExceptionState & exceptionState)284 void ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionState& exceptionState)
285 {
286 // Check that this node is not "floating".
287 // If it is, it can be deleted as a side effect of sending mutation events.
288 ASSERT(refCount() || parentOrShadowHostNode());
289
290 RefPtr<Node> protect(this);
291
292 if (oldChild == newChild) // nothing to do
293 return;
294
295 if (!oldChild) {
296 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
297 return;
298 }
299
300 // Make sure replacing the old child with the new is ok
301 if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
302 return;
303
304 // NotFoundError: Raised if oldChild is not a child of this node.
305 if (oldChild->parentNode() != this) {
306 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
307 return;
308 }
309
310 ChildListMutationScope mutation(*this);
311
312 RefPtr<Node> next = oldChild->nextSibling();
313
314 // Remove the node we're replacing
315 RefPtr<Node> removedChild = oldChild;
316 removeChild(oldChild, exceptionState);
317 if (exceptionState.hadException())
318 return;
319
320 if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
321 return;
322
323 // Does this one more time because removeChild() fires a MutationEvent.
324 if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
325 return;
326
327 NodeVector targets;
328 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
329 if (exceptionState.hadException())
330 return;
331
332 // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
333 if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
334 return;
335
336 InspectorInstrumentation::willInsertDOMNode(this);
337
338 // Add the new child(ren)
339 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
340 ASSERT(*it);
341 Node& child = **it;
342
343 // Due to arbitrary code running in response to a DOM mutation event it's
344 // possible that "next" is no longer a child of "this".
345 // It's also possible that "child" has been inserted elsewhere.
346 // In either of those cases, we'll just stop.
347 if (next && next->parentNode() != this)
348 break;
349 if (child.parentNode())
350 break;
351
352 treeScope().adoptIfNeeded(child);
353
354 // Add child before "next".
355 {
356 NoEventDispatchAssertion assertNoEventDispatch;
357 if (next)
358 insertBeforeCommon(*next, child);
359 else
360 appendChildToContainer(child, *this);
361 }
362
363 updateTreeAfterInsertion(child);
364 }
365
366 dispatchSubtreeModifiedEvent();
367 }
368
willRemoveChild(Node & child)369 void ContainerNode::willRemoveChild(Node& child)
370 {
371 ASSERT(child.parentNode() == this);
372 ChildListMutationScope(*this).willRemoveChild(child);
373 child.notifyMutationObserversNodeWillDetach();
374 dispatchChildRemovalEvents(child);
375 document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.
376 ChildFrameDisconnector(child).disconnect();
377 }
378
willRemoveChildren()379 void ContainerNode::willRemoveChildren()
380 {
381 NodeVector children;
382 getChildNodes(*this, children);
383
384 ChildListMutationScope mutation(*this);
385 for (NodeVector::const_iterator it = children.begin(); it != children.end(); it++) {
386 ASSERT(*it);
387 Node& child = **it;
388 mutation.willRemoveChild(child);
389 child.notifyMutationObserversNodeWillDetach();
390 dispatchChildRemovalEvents(child);
391 }
392
393 ChildFrameDisconnector(*this).disconnect(ChildFrameDisconnector::DescendantsOnly);
394 }
395
disconnectDescendantFrames()396 void ContainerNode::disconnectDescendantFrames()
397 {
398 ChildFrameDisconnector(*this).disconnect();
399 }
400
removeChild(Node * oldChild,ExceptionState & exceptionState)401 void ContainerNode::removeChild(Node* oldChild, ExceptionState& exceptionState)
402 {
403 // Check that this node is not "floating".
404 // If it is, it can be deleted as a side effect of sending mutation events.
405 ASSERT(refCount() || parentOrShadowHostNode());
406
407 RefPtr<Node> protect(this);
408
409 // NotFoundError: Raised if oldChild is not a child of this node.
410 if (!oldChild || oldChild->parentNode() != this) {
411 exceptionState.throwDOMException(NotFoundError, "The node to be removed is not a child of this node.");
412 return;
413 }
414
415 RefPtr<Node> child = oldChild;
416
417 document().removeFocusedElementOfSubtree(child.get());
418
419 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(&document()))
420 fullscreen->removeFullScreenElementOfSubtree(child.get());
421
422 // Events fired when blurring currently focused node might have moved this
423 // child into a different parent.
424 if (child->parentNode() != this) {
425 exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in a 'blur' event handler?");
426 return;
427 }
428
429 willRemoveChild(*child);
430
431 // Mutation events might have moved this child into a different parent.
432 if (child->parentNode() != this) {
433 exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in response to a mutation?");
434 return;
435 }
436
437 {
438 RenderWidget::UpdateSuspendScope suspendWidgetHierarchyUpdates;
439
440 Node* prev = child->previousSibling();
441 Node* next = child->nextSibling();
442 removeBetween(prev, next, *child);
443 childrenChanged(false, prev, next, -1);
444 ChildNodeRemovalNotifier(*this).notify(*child);
445 }
446 dispatchSubtreeModifiedEvent();
447 }
448
removeBetween(Node * previousChild,Node * nextChild,Node & oldChild)449 void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
450 {
451 NoEventDispatchAssertion assertNoEventDispatch;
452
453 ASSERT(oldChild.parentNode() == this);
454
455 if (!oldChild.needsAttach())
456 oldChild.detach();
457
458 if (nextChild)
459 nextChild->setPreviousSibling(previousChild);
460 if (previousChild)
461 previousChild->setNextSibling(nextChild);
462 if (m_firstChild == oldChild)
463 m_firstChild = nextChild;
464 if (m_lastChild == oldChild)
465 m_lastChild = previousChild;
466
467 oldChild.setPreviousSibling(0);
468 oldChild.setNextSibling(0);
469 oldChild.setParentOrShadowHostNode(0);
470
471 document().adoptIfNeeded(oldChild);
472 }
473
parserRemoveChild(Node & oldChild)474 void ContainerNode::parserRemoveChild(Node& oldChild)
475 {
476 ASSERT(oldChild.parentNode() == this);
477 ASSERT(!oldChild.isDocumentFragment());
478
479 Node* prev = oldChild.previousSibling();
480 Node* next = oldChild.nextSibling();
481
482 oldChild.updateAncestorConnectedSubframeCountForRemoval();
483
484 ChildListMutationScope(*this).willRemoveChild(oldChild);
485 oldChild.notifyMutationObserversNodeWillDetach();
486
487 removeBetween(prev, next, oldChild);
488
489 childrenChanged(true, prev, next, -1);
490 ChildNodeRemovalNotifier(*this).notify(oldChild);
491 }
492
493 // this differs from other remove functions because it forcibly removes all the children,
494 // regardless of read-only status or event exceptions, e.g.
removeChildren()495 void ContainerNode::removeChildren()
496 {
497 if (!m_firstChild)
498 return;
499
500 // The container node can be removed from event handlers.
501 RefPtr<ContainerNode> protect(this);
502
503 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(&document()))
504 fullscreen->removeFullScreenElementOfSubtree(this, true);
505
506 // Do any prep work needed before actually starting to detach
507 // and remove... e.g. stop loading frames, fire unload events.
508 willRemoveChildren();
509
510 {
511 // Removing focus can cause frames to load, either via events (focusout, blur)
512 // or widget updates (e.g., for <embed>).
513 SubframeLoadingDisabler disabler(*this);
514
515 // Exclude this node when looking for removed focusedElement since only
516 // children will be removed.
517 // This must be later than willRemoveChildren, which might change focus
518 // state of a child.
519 document().removeFocusedElementOfSubtree(this, true);
520
521 // Removing a node from a selection can cause widget updates.
522 document().nodeChildrenWillBeRemoved(this);
523 }
524
525
526 NodeVector removedChildren;
527 {
528 RenderWidget::UpdateSuspendScope suspendWidgetHierarchyUpdates;
529 {
530 NoEventDispatchAssertion assertNoEventDispatch;
531 removedChildren.reserveInitialCapacity(childNodeCount());
532 while (m_firstChild) {
533 removedChildren.append(m_firstChild);
534 removeBetween(0, m_firstChild->nextSibling(), *m_firstChild);
535 }
536 }
537
538 childrenChanged(false, 0, 0, -static_cast<int>(removedChildren.size()));
539
540 for (size_t i = 0; i < removedChildren.size(); ++i)
541 ChildNodeRemovalNotifier(*this).notify(*removedChildren[i]);
542 }
543
544 dispatchSubtreeModifiedEvent();
545 }
546
appendChild(PassRefPtr<Node> newChild,ExceptionState & exceptionState)547 void ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionState& exceptionState)
548 {
549 RefPtr<ContainerNode> protect(this);
550
551 // Check that this node is not "floating".
552 // If it is, it can be deleted as a side effect of sending mutation events.
553 ASSERT(refCount() || parentOrShadowHostNode());
554
555 // Make sure adding the new child is ok
556 if (!checkAcceptChild(newChild.get(), 0, exceptionState))
557 return;
558 ASSERT(newChild);
559
560 if (newChild == m_lastChild) // nothing to do
561 return;
562
563 NodeVector targets;
564 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
565 if (exceptionState.hadException())
566 return;
567
568 if (targets.isEmpty())
569 return;
570
571 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
572 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState))
573 return;
574
575 InspectorInstrumentation::willInsertDOMNode(this);
576
577 // Now actually add the child(ren)
578 ChildListMutationScope mutation(*this);
579 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
580 ASSERT(*it);
581 Node& child = **it;
582
583 // If the child has a parent again, just stop what we're doing, because
584 // that means someone is doing something with DOM mutation -- can't re-parent
585 // a child that already has a parent.
586 if (child.parentNode())
587 break;
588
589 treeScope().adoptIfNeeded(child);
590
591 // Append child to the end of the list
592 {
593 NoEventDispatchAssertion assertNoEventDispatch;
594 appendChildToContainer(child, *this);
595 }
596
597 updateTreeAfterInsertion(child);
598 }
599
600 dispatchSubtreeModifiedEvent();
601 }
602
parserAppendChild(PassRefPtr<Node> newChild)603 void ContainerNode::parserAppendChild(PassRefPtr<Node> newChild)
604 {
605 ASSERT(newChild);
606 ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
607 ASSERT(!newChild->isDocumentFragment());
608 ASSERT(!hasTagName(HTMLNames::templateTag));
609
610 if (document() != newChild->document())
611 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
612
613 Node* last = m_lastChild;
614 {
615 NoEventDispatchAssertion assertNoEventDispatch;
616 // FIXME: This method should take a PassRefPtr.
617 appendChildToContainer(*newChild, *this);
618 treeScope().adoptIfNeeded(*newChild);
619 }
620
621 newChild->updateAncestorConnectedSubframeCountForInsertion();
622
623 ChildListMutationScope(*this).childAdded(*newChild);
624
625 childrenChanged(true, last, 0, 1);
626 ChildNodeInsertionNotifier(*this).notify(*newChild);
627 }
628
attach(const AttachContext & context)629 void ContainerNode::attach(const AttachContext& context)
630 {
631 attachChildren(context);
632 clearChildNeedsStyleRecalc();
633 Node::attach(context);
634 }
635
detach(const AttachContext & context)636 void ContainerNode::detach(const AttachContext& context)
637 {
638 detachChildren(context);
639 clearChildNeedsStyleRecalc();
640 Node::detach(context);
641 }
642
childrenChanged(bool changedByParser,Node *,Node *,int childCountDelta)643 void ContainerNode::childrenChanged(bool changedByParser, Node*, Node*, int childCountDelta)
644 {
645 document().incDOMTreeVersion();
646 if (!changedByParser && childCountDelta)
647 document().updateRangesAfterChildrenChanged(this);
648 invalidateNodeListCachesInAncestors();
649 if (childCountDelta > 0 && inActiveDocument()) {
650 setChildNeedsStyleRecalc();
651 markAncestorsWithChildNeedsStyleRecalc();
652 }
653 }
654
cloneChildNodes(ContainerNode * clone)655 void ContainerNode::cloneChildNodes(ContainerNode *clone)
656 {
657 TrackExceptionState exceptionState;
658 for (Node* n = firstChild(); n && !exceptionState.hadException(); n = n->nextSibling())
659 clone->appendChild(n->cloneNode(true), exceptionState);
660 }
661
662
getUpperLeftCorner(FloatPoint & point) const663 bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const
664 {
665 if (!renderer())
666 return false;
667 // What is this code really trying to do?
668 RenderObject* o = renderer();
669 RenderObject* p = o;
670
671 if (!o->isInline() || o->isReplaced()) {
672 point = o->localToAbsolute(FloatPoint(), UseTransforms);
673 return true;
674 }
675
676 // find the next text/image child, to get a position
677 while (o) {
678 p = o;
679 if (o->firstChild()) {
680 o = o->firstChild();
681 } else if (o->nextSibling()) {
682 o = o->nextSibling();
683 } else {
684 RenderObject* next = 0;
685 while (!next && o->parent()) {
686 o = o->parent();
687 next = o->nextSibling();
688 }
689 o = next;
690
691 if (!o)
692 break;
693 }
694 ASSERT(o);
695
696 if (!o->isInline() || o->isReplaced()) {
697 point = o->localToAbsolute(FloatPoint(), UseTransforms);
698 return true;
699 }
700
701 if (p->node() && p->node() == this && o->isText() && !o->isBR() && !toRenderText(o)->firstTextBox()) {
702 // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor
703 } else if ((o->isText() && !o->isBR()) || o->isReplaced()) {
704 point = FloatPoint();
705 if (o->isText() && toRenderText(o)->firstTextBox()) {
706 point.move(toRenderText(o)->linesBoundingBox().x(), toRenderText(o)->firstTextBox()->root()->lineTop());
707 } else if (o->isBox()) {
708 RenderBox* box = toRenderBox(o);
709 point.moveBy(box->location());
710 }
711 point = o->container()->localToAbsolute(point, UseTransforms);
712 return true;
713 }
714 }
715
716 // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
717 // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
718 if (!o && document().view()) {
719 point = FloatPoint(0, document().view()->contentsHeight());
720 return true;
721 }
722 return false;
723 }
724
getLowerRightCorner(FloatPoint & point) const725 bool ContainerNode::getLowerRightCorner(FloatPoint& point) const
726 {
727 if (!renderer())
728 return false;
729
730 RenderObject* o = renderer();
731 if (!o->isInline() || o->isReplaced()) {
732 RenderBox* box = toRenderBox(o);
733 point = o->localToAbsolute(LayoutPoint(box->size()), UseTransforms);
734 return true;
735 }
736
737 // find the last text/image child, to get a position
738 while (o) {
739 if (o->lastChild()) {
740 o = o->lastChild();
741 } else if (o->previousSibling()) {
742 o = o->previousSibling();
743 } else {
744 RenderObject* prev = 0;
745 while (!prev) {
746 o = o->parent();
747 if (!o)
748 return false;
749 prev = o->previousSibling();
750 }
751 o = prev;
752 }
753 ASSERT(o);
754 if (o->isText() || o->isReplaced()) {
755 point = FloatPoint();
756 if (o->isText()) {
757 RenderText* text = toRenderText(o);
758 IntRect linesBox = text->linesBoundingBox();
759 if (!linesBox.maxX() && !linesBox.maxY())
760 continue;
761 point.moveBy(linesBox.maxXMaxYCorner());
762 } else {
763 RenderBox* box = toRenderBox(o);
764 point.moveBy(box->frameRect().maxXMaxYCorner());
765 }
766 point = o->container()->localToAbsolute(point, UseTransforms);
767 return true;
768 }
769 }
770 return true;
771 }
772
773 // FIXME: This override is only needed for inline anchors without an
774 // InlineBox and it does not belong in ContainerNode as it reaches into
775 // the render and line box trees.
776 // https://code.google.com/p/chromium/issues/detail?id=248354
boundingBox() const777 LayoutRect ContainerNode::boundingBox() const
778 {
779 FloatPoint upperLeft, lowerRight;
780 bool foundUpperLeft = getUpperLeftCorner(upperLeft);
781 bool foundLowerRight = getLowerRightCorner(lowerRight);
782
783 // If we've found one corner, but not the other,
784 // then we should just return a point at the corner that we did find.
785 if (foundUpperLeft != foundLowerRight) {
786 if (foundUpperLeft)
787 lowerRight = upperLeft;
788 else
789 upperLeft = lowerRight;
790 }
791
792 return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft));
793 }
794
795 // This is used by FrameSelection to denote when the active-state of the page has changed
796 // independent of the focused element changing.
focusStateChanged()797 void ContainerNode::focusStateChanged()
798 {
799 // If we're just changing the window's active state and the focused node has no
800 // renderer we can just ignore the state change.
801 if (!renderer())
802 return;
803 // FIXME: This could probably setNeedsStyleRecalc(LocalStyleChange) in the affectedByFocus case
804 // and only setNeedsStyleRecalc(SubtreeStyleChange) in the childrenAffectedByFocus case.
805 if (renderStyle()->affectedByFocus() || (isElementNode() && toElement(this)->childrenAffectedByFocus()))
806 setNeedsStyleRecalc();
807 if (renderer() && renderer()->style()->hasAppearance())
808 RenderTheme::theme().stateChanged(renderer(), FocusState);
809 }
810
setFocus(bool received)811 void ContainerNode::setFocus(bool received)
812 {
813 if (focused() == received)
814 return;
815
816 Node::setFocus(received);
817
818 focusStateChanged();
819 // If :focus sets display: none, we lose focus but still need to recalc our style.
820 if (!renderer() && !received)
821 setNeedsStyleRecalc();
822 }
823
setActive(bool down)824 void ContainerNode::setActive(bool down)
825 {
826 if (down == active())
827 return;
828
829 Node::setActive(down);
830
831 // FIXME: Why does this not need to handle the display: none transition like :hover does?
832 if (renderer()) {
833 if (renderStyle()->affectedByActive() || (isElementNode() && toElement(this)->childrenAffectedByActive()))
834 setNeedsStyleRecalc();
835 if (renderStyle()->hasAppearance())
836 RenderTheme::theme().stateChanged(renderer(), PressedState);
837 }
838 }
839
setHovered(bool over)840 void ContainerNode::setHovered(bool over)
841 {
842 if (over == hovered())
843 return;
844
845 Node::setHovered(over);
846
847 // If :hover sets display: none we lose our hover but still need to recalc our style.
848 if (!renderer()) {
849 if (!over)
850 setNeedsStyleRecalc();
851 return;
852 }
853
854 if (renderer()) {
855 if (renderStyle()->affectedByHover() || (isElementNode() && toElement(this)->childrenAffectedByHover()))
856 setNeedsStyleRecalc();
857 if (renderer() && renderer()->style()->hasAppearance())
858 RenderTheme::theme().stateChanged(renderer(), HoverState);
859 }
860 }
861
children()862 PassRefPtr<HTMLCollection> ContainerNode::children()
863 {
864 return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLCollection>(this, NodeChildren);
865 }
866
firstElementChild() const867 Element* ContainerNode::firstElementChild() const
868 {
869 return ElementTraversal::firstWithin(*this);
870 }
871
lastElementChild() const872 Element* ContainerNode::lastElementChild() const
873 {
874 Node* n = lastChild();
875 while (n && !n->isElementNode())
876 n = n->previousSibling();
877 return toElement(n);
878 }
879
childElementCount() const880 unsigned ContainerNode::childElementCount() const
881 {
882 unsigned count = 0;
883 Node* n = firstChild();
884 while (n) {
885 count += n->isElementNode();
886 n = n->nextSibling();
887 }
888 return count;
889 }
890
childNodeCount() const891 unsigned ContainerNode::childNodeCount() const
892 {
893 unsigned count = 0;
894 Node *n;
895 for (n = firstChild(); n; n = n->nextSibling())
896 count++;
897 return count;
898 }
899
childNode(unsigned index) const900 Node *ContainerNode::childNode(unsigned index) const
901 {
902 unsigned i;
903 Node *n = firstChild();
904 for (i = 0; n != 0 && i < index; i++)
905 n = n->nextSibling();
906 return n;
907 }
908
dispatchChildInsertionEvents(Node & child)909 static void dispatchChildInsertionEvents(Node& child)
910 {
911 if (child.isInShadowTree())
912 return;
913
914 ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
915
916 RefPtr<Node> c(child);
917 RefPtr<Document> document(child.document());
918
919 if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
920 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInserted, true, c->parentNode()));
921
922 // dispatch the DOMNodeInsertedIntoDocument event to all descendants
923 if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
924 for (; c; c = NodeTraversal::next(*c, &child))
925 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInsertedIntoDocument, false));
926 }
927 }
928
dispatchChildRemovalEvents(Node & child)929 static void dispatchChildRemovalEvents(Node& child)
930 {
931 if (child.isInShadowTree()) {
932 InspectorInstrumentation::willRemoveDOMNode(&child);
933 return;
934 }
935
936 ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
937
938 InspectorInstrumentation::willRemoveDOMNode(&child);
939
940 RefPtr<Node> c(child);
941 RefPtr<Document> document(child.document());
942
943 // dispatch pre-removal mutation events
944 if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) {
945 NodeChildRemovalTracker scope(child);
946 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemoved, true, c->parentNode()));
947 }
948
949 // dispatch the DOMNodeRemovedFromDocument event to all descendants
950 if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
951 NodeChildRemovalTracker scope(child);
952 for (; c; c = NodeTraversal::next(*c, &child))
953 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemovedFromDocument, false));
954 }
955 }
956
updateTreeAfterInsertion(Node & child)957 void ContainerNode::updateTreeAfterInsertion(Node& child)
958 {
959 ASSERT(refCount());
960 ASSERT(child.refCount());
961
962 ChildListMutationScope(*this).childAdded(child);
963
964 childrenChanged(false, child.previousSibling(), child.nextSibling(), 1);
965
966 ChildNodeInsertionNotifier(*this).notify(child);
967
968 dispatchChildInsertionEvents(child);
969 }
970
971 #ifndef NDEBUG
childAttachedAllowedWhenAttachingChildren(ContainerNode * node)972 bool childAttachedAllowedWhenAttachingChildren(ContainerNode* node)
973 {
974 if (node->isShadowRoot())
975 return true;
976
977 if (node->isInsertionPoint())
978 return true;
979
980 if (node->isElementNode() && toElement(node)->shadow())
981 return true;
982
983 return false;
984 }
985 #endif
986
987 } // namespace WebCore
988