• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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