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, 2013 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/core/v8/ExceptionState.h"
27 #include "core/dom/ChildFrameDisconnector.h"
28 #include "core/dom/ChildListMutationScope.h"
29 #include "core/dom/ClassCollection.h"
30 #include "core/dom/ElementTraversal.h"
31 #include "core/dom/ExceptionCode.h"
32 #include "core/dom/NameNodeList.h"
33 #include "core/dom/NodeChildRemovalTracker.h"
34 #include "core/dom/NodeRareData.h"
35 #include "core/dom/NodeRenderStyle.h"
36 #include "core/dom/NodeTraversal.h"
37 #include "core/dom/SelectorQuery.h"
38 #include "core/dom/StaticNodeList.h"
39 #include "core/dom/StyleEngine.h"
40 #include "core/dom/shadow/ElementShadow.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/events/MutationEvent.h"
43 #include "core/html/HTMLCollection.h"
44 #include "core/html/HTMLFrameOwnerElement.h"
45 #include "core/html/HTMLTagCollection.h"
46 #include "core/html/RadioNodeList.h"
47 #include "core/inspector/InspectorInstrumentation.h"
48 #include "core/rendering/InlineTextBox.h"
49 #include "core/rendering/RenderText.h"
50 #include "core/rendering/RenderTheme.h"
51 #include "core/rendering/RenderView.h"
52 #include "platform/EventDispatchForbiddenScope.h"
53 #include "platform/ScriptForbiddenScope.h"
54
55 namespace blink {
56
57 using namespace HTMLNames;
58
59 static void dispatchChildInsertionEvents(Node&);
60 static void dispatchChildRemovalEvents(Node&);
61
62 #if ENABLE(ASSERT)
63 unsigned EventDispatchForbiddenScope::s_count = 0;
64 #endif
65
collectChildrenAndRemoveFromOldParent(Node & node,NodeVector & nodes,ExceptionState & exceptionState)66 static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionState& exceptionState)
67 {
68 if (node.isDocumentFragment()) {
69 DocumentFragment& fragment = toDocumentFragment(node);
70 getChildNodes(fragment, nodes);
71 fragment.removeChildren();
72 return;
73 }
74 nodes.append(&node);
75 if (ContainerNode* oldParent = node.parentNode())
76 oldParent->removeChild(&node, exceptionState);
77 }
78
79 #if !ENABLE(OILPAN)
removeDetachedChildren()80 void ContainerNode::removeDetachedChildren()
81 {
82 ASSERT(!connectedSubframeCount());
83 ASSERT(needsAttach());
84 removeDetachedChildrenInContainer(*this);
85 }
86 #endif
87
parserTakeAllChildrenFrom(ContainerNode & oldParent)88 void ContainerNode::parserTakeAllChildrenFrom(ContainerNode& oldParent)
89 {
90 while (RefPtrWillBeRawPtr<Node> child = oldParent.firstChild()) {
91 oldParent.parserRemoveChild(*child);
92 treeScope().adoptIfNeeded(*child);
93 parserAppendChild(child.get());
94 }
95 }
96
~ContainerNode()97 ContainerNode::~ContainerNode()
98 {
99 ASSERT(needsAttach());
100 #if !ENABLE(OILPAN)
101 willBeDeletedFromDocument();
102 removeDetachedChildren();
103 #endif
104 }
105
isChildTypeAllowed(const Node & child) const106 bool ContainerNode::isChildTypeAllowed(const Node& child) const
107 {
108 if (!child.isDocumentFragment())
109 return childTypeAllowed(child.nodeType());
110
111 for (Node* node = toDocumentFragment(child).firstChild(); node; node = node->nextSibling()) {
112 if (!childTypeAllowed(node->nodeType()))
113 return false;
114 }
115 return true;
116 }
117
containsConsideringHostElements(const Node & newChild) const118 bool ContainerNode::containsConsideringHostElements(const Node& newChild) const
119 {
120 if (isInShadowTree() || document().isTemplateDocument())
121 return newChild.containsIncludingHostElements(*this);
122 return newChild.contains(this);
123 }
124
checkAcceptChild(const Node * newChild,const Node * oldChild,ExceptionState & exceptionState) const125 bool ContainerNode::checkAcceptChild(const Node* newChild, const Node* oldChild, ExceptionState& exceptionState) const
126 {
127 // Not mentioned in spec: throw NotFoundError if newChild is null
128 if (!newChild) {
129 exceptionState.throwDOMException(NotFoundError, "The new child element is null.");
130 return false;
131 }
132
133 // Use common case fast path if possible.
134 if ((newChild->isElementNode() || newChild->isTextNode()) && isElementNode()) {
135 ASSERT(isChildTypeAllowed(*newChild));
136 if (containsConsideringHostElements(*newChild)) {
137 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
138 return false;
139 }
140 return true;
141 }
142
143 // This should never happen, but also protect release builds from tree corruption.
144 ASSERT(!newChild->isPseudoElement());
145 if (newChild->isPseudoElement()) {
146 exceptionState.throwDOMException(HierarchyRequestError, "The new child element is a pseudo-element.");
147 return false;
148 }
149
150 if (containsConsideringHostElements(*newChild)) {
151 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
152 return false;
153 }
154
155 if (oldChild && isDocumentNode()) {
156 if (!toDocument(this)->canReplaceChild(*newChild, *oldChild)) {
157 // FIXME: Adjust 'Document::canReplaceChild' to return some additional detail (or an error message).
158 exceptionState.throwDOMException(HierarchyRequestError, "Failed to replace child.");
159 return false;
160 }
161 } else if (!isChildTypeAllowed(*newChild)) {
162 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type '" + newChild->nodeName() + "' may not be inserted inside nodes of type '" + nodeName() + "'.");
163 return false;
164 }
165
166 return true;
167 }
168
checkAcceptChildGuaranteedNodeTypes(const Node & newChild,ExceptionState & exceptionState) const169 bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node& newChild, ExceptionState& exceptionState) const
170 {
171 ASSERT(isChildTypeAllowed(newChild));
172 if (newChild.contains(this)) {
173 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
174 return false;
175 }
176 return true;
177 }
178
insertBefore(PassRefPtrWillBeRawPtr<Node> newChild,Node * refChild,ExceptionState & exceptionState)179 PassRefPtrWillBeRawPtr<Node> ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* refChild, ExceptionState& exceptionState)
180 {
181 #if !ENABLE(OILPAN)
182 // Check that this node is not "floating".
183 // If it is, it can be deleted as a side effect of sending mutation events.
184 ASSERT(refCount() || parentOrShadowHostNode());
185 #endif
186
187 RefPtrWillBeRawPtr<Node> protect(this);
188
189 // insertBefore(node, 0) is equivalent to appendChild(node)
190 if (!refChild) {
191 return appendChild(newChild, exceptionState);
192 }
193
194 // Make sure adding the new child is OK.
195 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
196 if (exceptionState.hadException())
197 return nullptr;
198 return newChild;
199 }
200 ASSERT(newChild);
201
202 // NotFoundError: Raised if refChild is not a child of this node
203 if (refChild->parentNode() != this) {
204 exceptionState.throwDOMException(NotFoundError, "The node before which the new node is to be inserted is not a child of this node.");
205 return nullptr;
206 }
207
208 // nothing to do
209 if (refChild->previousSibling() == newChild || refChild == newChild)
210 return newChild;
211
212 RefPtrWillBeRawPtr<Node> next = refChild;
213
214 NodeVector targets;
215 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
216 if (exceptionState.hadException())
217 return nullptr;
218 if (targets.isEmpty())
219 return newChild;
220
221 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
222 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
223 if (exceptionState.hadException())
224 return nullptr;
225 return newChild;
226 }
227
228 InspectorInstrumentation::willInsertDOMNode(this);
229
230 ChildListMutationScope mutation(*this);
231 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
232 ASSERT(*it);
233 Node& child = **it;
234
235 // Due to arbitrary code running in response to a DOM mutation event it's
236 // possible that "next" is no longer a child of "this".
237 // It's also possible that "child" has been inserted elsewhere.
238 // In either of those cases, we'll just stop.
239 if (next->parentNode() != this)
240 break;
241 if (child.parentNode())
242 break;
243
244 treeScope().adoptIfNeeded(child);
245
246 insertBeforeCommon(*next, child);
247
248 updateTreeAfterInsertion(child);
249 }
250
251 dispatchSubtreeModifiedEvent();
252
253 return newChild;
254 }
255
insertBeforeCommon(Node & nextChild,Node & newChild)256 void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
257 {
258 EventDispatchForbiddenScope assertNoEventDispatch;
259 ScriptForbiddenScope forbidScript;
260
261 ASSERT(!newChild.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
262 ASSERT(!newChild.nextSibling());
263 ASSERT(!newChild.previousSibling());
264 ASSERT(!newChild.isShadowRoot());
265
266 Node* prev = nextChild.previousSibling();
267 ASSERT(m_lastChild != prev);
268 nextChild.setPreviousSibling(&newChild);
269 if (prev) {
270 ASSERT(firstChild() != nextChild);
271 ASSERT(prev->nextSibling() == nextChild);
272 prev->setNextSibling(&newChild);
273 } else {
274 ASSERT(firstChild() == nextChild);
275 m_firstChild = &newChild;
276 }
277 newChild.setParentOrShadowHostNode(this);
278 newChild.setPreviousSibling(prev);
279 newChild.setNextSibling(&nextChild);
280 }
281
appendChildCommon(Node & child)282 void ContainerNode::appendChildCommon(Node& child)
283 {
284 child.setParentOrShadowHostNode(this);
285
286 if (m_lastChild) {
287 child.setPreviousSibling(m_lastChild);
288 m_lastChild->setNextSibling(&child);
289 } else {
290 setFirstChild(&child);
291 }
292
293 setLastChild(&child);
294 }
295
parserInsertBefore(PassRefPtrWillBeRawPtr<Node> newChild,Node & nextChild)296 void ContainerNode::parserInsertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node& nextChild)
297 {
298 ASSERT(newChild);
299 ASSERT(nextChild.parentNode() == this);
300 ASSERT(!newChild->isDocumentFragment());
301 ASSERT(!isHTMLTemplateElement(this));
302
303 if (nextChild.previousSibling() == newChild || &nextChild == newChild) // nothing to do
304 return;
305
306 RefPtrWillBeRawPtr<Node> protect(this);
307
308 if (document() != newChild->document())
309 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
310
311 insertBeforeCommon(nextChild, *newChild);
312
313 newChild->updateAncestorConnectedSubframeCountForInsertion();
314
315 ChildListMutationScope(*this).childAdded(*newChild);
316
317 notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
318 }
319
replaceChild(PassRefPtrWillBeRawPtr<Node> newChild,PassRefPtrWillBeRawPtr<Node> oldChild,ExceptionState & exceptionState)320 PassRefPtrWillBeRawPtr<Node> ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
321 {
322 #if !ENABLE(OILPAN)
323 // Check that this node is not "floating".
324 // If it is, it can be deleted as a side effect of sending mutation events.
325 ASSERT(refCount() || parentOrShadowHostNode());
326 #endif
327
328 RefPtrWillBeRawPtr<Node> protect(this);
329
330 if (oldChild == newChild) // nothing to do
331 return oldChild;
332
333 if (!oldChild) {
334 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
335 return nullptr;
336 }
337
338 RefPtrWillBeRawPtr<Node> child = oldChild;
339
340 // Make sure replacing the old child with the new is ok
341 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
342 if (exceptionState.hadException())
343 return nullptr;
344 return child;
345 }
346
347 // NotFoundError: Raised if oldChild is not a child of this node.
348 if (child->parentNode() != this) {
349 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
350 return nullptr;
351 }
352
353 ChildListMutationScope mutation(*this);
354
355 RefPtrWillBeRawPtr<Node> next = child->nextSibling();
356
357 // Remove the node we're replacing
358 removeChild(child, exceptionState);
359 if (exceptionState.hadException())
360 return nullptr;
361
362 if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
363 return child;
364
365 // Does this one more time because removeChild() fires a MutationEvent.
366 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
367 if (exceptionState.hadException())
368 return nullptr;
369 return child;
370 }
371
372 NodeVector targets;
373 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
374 if (exceptionState.hadException())
375 return nullptr;
376
377 // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
378 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
379 if (exceptionState.hadException())
380 return nullptr;
381 return child;
382 }
383
384 InspectorInstrumentation::willInsertDOMNode(this);
385
386 // Add the new child(ren)
387 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
388 ASSERT(*it);
389 Node& child = **it;
390
391 // Due to arbitrary code running in response to a DOM mutation event it's
392 // possible that "next" is no longer a child of "this".
393 // It's also possible that "child" has been inserted elsewhere.
394 // In either of those cases, we'll just stop.
395 if (next && next->parentNode() != this)
396 break;
397 if (child.parentNode())
398 break;
399
400 treeScope().adoptIfNeeded(child);
401
402 // Add child before "next".
403 {
404 EventDispatchForbiddenScope assertNoEventDispatch;
405 if (next)
406 insertBeforeCommon(*next, child);
407 else
408 appendChildCommon(child);
409 }
410
411 updateTreeAfterInsertion(child);
412 }
413
414 dispatchSubtreeModifiedEvent();
415 return child;
416 }
417
willRemoveChild(Node & child)418 void ContainerNode::willRemoveChild(Node& child)
419 {
420 ASSERT(child.parentNode() == this);
421 ChildListMutationScope(*this).willRemoveChild(child);
422 child.notifyMutationObserversNodeWillDetach();
423 dispatchChildRemovalEvents(child);
424 document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.
425 ChildFrameDisconnector(child).disconnect();
426 }
427
willRemoveChildren()428 void ContainerNode::willRemoveChildren()
429 {
430 NodeVector children;
431 getChildNodes(*this, children);
432
433 ChildListMutationScope mutation(*this);
434 for (NodeVector::const_iterator it = children.begin(); it != children.end(); ++it) {
435 ASSERT(*it);
436 Node& child = **it;
437 mutation.willRemoveChild(child);
438 child.notifyMutationObserversNodeWillDetach();
439 dispatchChildRemovalEvents(child);
440 }
441
442 ChildFrameDisconnector(*this).disconnect(ChildFrameDisconnector::DescendantsOnly);
443 }
444
445 #if !ENABLE(OILPAN)
removeDetachedChildrenInContainer(ContainerNode & container)446 void ContainerNode::removeDetachedChildrenInContainer(ContainerNode& container)
447 {
448 // List of nodes to be deleted.
449 Node* head = 0;
450 Node* tail = 0;
451
452 addChildNodesToDeletionQueue(head, tail, container);
453
454 Node* n;
455 Node* next;
456 while ((n = head) != 0) {
457 ASSERT_WITH_SECURITY_IMPLICATION(n->m_deletionHasBegun);
458
459 next = n->nextSibling();
460 n->setNextSibling(0);
461
462 head = next;
463 if (next == 0)
464 tail = 0;
465
466 if (n->hasChildren())
467 addChildNodesToDeletionQueue(head, tail, toContainerNode(*n));
468
469 delete n;
470 }
471 }
472
addChildNodesToDeletionQueue(Node * & head,Node * & tail,ContainerNode & container)473 void ContainerNode::addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
474 {
475 // We have to tell all children that their parent has died.
476 Node* next = 0;
477 for (Node* n = container.firstChild(); n; n = next) {
478 ASSERT_WITH_SECURITY_IMPLICATION(!n->m_deletionHasBegun);
479
480 next = n->nextSibling();
481 n->setNextSibling(0);
482 n->setParentOrShadowHostNode(0);
483 container.setFirstChild(next);
484 if (next)
485 next->setPreviousSibling(0);
486
487 if (!n->refCount()) {
488 #if ENABLE(SECURITY_ASSERT)
489 n->m_deletionHasBegun = true;
490 #endif
491 // Add the node to the list of nodes to be deleted.
492 // Reuse the nextSibling pointer for this purpose.
493 if (tail)
494 tail->setNextSibling(n);
495 else
496 head = n;
497
498 tail = n;
499 } else {
500 RefPtrWillBeRawPtr<Node> protect(n); // removedFromDocument may remove all references to this node.
501 container.document().adoptIfNeeded(*n);
502 if (n->inDocument())
503 container.notifyNodeRemoved(*n);
504 }
505 }
506
507 container.setLastChild(0);
508 }
509 #endif
510
disconnectDescendantFrames()511 void ContainerNode::disconnectDescendantFrames()
512 {
513 ChildFrameDisconnector(*this).disconnect();
514 }
515
trace(Visitor * visitor)516 void ContainerNode::trace(Visitor* visitor)
517 {
518 visitor->trace(m_firstChild);
519 visitor->trace(m_lastChild);
520 Node::trace(visitor);
521 }
522
removeChild(PassRefPtrWillBeRawPtr<Node> oldChild,ExceptionState & exceptionState)523 PassRefPtrWillBeRawPtr<Node> ContainerNode::removeChild(PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
524 {
525 #if !ENABLE(OILPAN)
526 // Check that this node is not "floating".
527 // If it is, it can be deleted as a side effect of sending mutation events.
528 ASSERT(refCount() || parentOrShadowHostNode());
529 #endif
530
531 RefPtrWillBeRawPtr<Node> protect(this);
532
533 // NotFoundError: Raised if oldChild is not a child of this node.
534 // FIXME: We should never really get PseudoElements in here, but editing will sometimes
535 // attempt to remove them still. We should fix that and enable this ASSERT.
536 // ASSERT(!oldChild->isPseudoElement())
537 if (!oldChild || oldChild->parentNode() != this || oldChild->isPseudoElement()) {
538 exceptionState.throwDOMException(NotFoundError, "The node to be removed is not a child of this node.");
539 return nullptr;
540 }
541
542 RefPtrWillBeRawPtr<Node> child = oldChild;
543
544 document().removeFocusedElementOfSubtree(child.get());
545
546 // Events fired when blurring currently focused node might have moved this
547 // child into a different parent.
548 if (child->parentNode() != this) {
549 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?");
550 return nullptr;
551 }
552
553 willRemoveChild(*child);
554
555 // Mutation events might have moved this child into a different parent.
556 if (child->parentNode() != this) {
557 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?");
558 return nullptr;
559 }
560
561 {
562 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates;
563
564 Node* prev = child->previousSibling();
565 Node* next = child->nextSibling();
566 removeBetween(prev, next, *child);
567 notifyNodeRemoved(*child);
568 childrenChanged(ChildrenChange::forRemoval(*child, prev, next, ChildrenChangeSourceAPI));
569 }
570 dispatchSubtreeModifiedEvent();
571 return child;
572 }
573
removeBetween(Node * previousChild,Node * nextChild,Node & oldChild)574 void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
575 {
576 EventDispatchForbiddenScope assertNoEventDispatch;
577
578 ASSERT(oldChild.parentNode() == this);
579
580 if (!oldChild.needsAttach())
581 oldChild.detach();
582
583 if (nextChild)
584 nextChild->setPreviousSibling(previousChild);
585 if (previousChild)
586 previousChild->setNextSibling(nextChild);
587 if (m_firstChild == &oldChild)
588 m_firstChild = nextChild;
589 if (m_lastChild == &oldChild)
590 m_lastChild = previousChild;
591
592 oldChild.setPreviousSibling(0);
593 oldChild.setNextSibling(0);
594 oldChild.setParentOrShadowHostNode(0);
595
596 document().adoptIfNeeded(oldChild);
597 }
598
parserRemoveChild(Node & oldChild)599 void ContainerNode::parserRemoveChild(Node& oldChild)
600 {
601 ASSERT(oldChild.parentNode() == this);
602 ASSERT(!oldChild.isDocumentFragment());
603
604 Node* prev = oldChild.previousSibling();
605 Node* next = oldChild.nextSibling();
606
607 oldChild.updateAncestorConnectedSubframeCountForRemoval();
608
609 ChildListMutationScope(*this).willRemoveChild(oldChild);
610 oldChild.notifyMutationObserversNodeWillDetach();
611
612 removeBetween(prev, next, oldChild);
613
614 notifyNodeRemoved(oldChild);
615 childrenChanged(ChildrenChange::forRemoval(oldChild, prev, next, ChildrenChangeSourceParser));
616 }
617
618 // this differs from other remove functions because it forcibly removes all the children,
619 // regardless of read-only status or event exceptions, e.g.
removeChildren()620 void ContainerNode::removeChildren()
621 {
622 if (!m_firstChild)
623 return;
624
625 // The container node can be removed from event handlers.
626 RefPtrWillBeRawPtr<ContainerNode> protect(this);
627
628 // Do any prep work needed before actually starting to detach
629 // and remove... e.g. stop loading frames, fire unload events.
630 willRemoveChildren();
631
632 {
633 // Removing focus can cause frames to load, either via events (focusout, blur)
634 // or widget updates (e.g., for <embed>).
635 SubframeLoadingDisabler disabler(*this);
636
637 // Exclude this node when looking for removed focusedElement since only
638 // children will be removed.
639 // This must be later than willRemoveChildren, which might change focus
640 // state of a child.
641 document().removeFocusedElementOfSubtree(this, true);
642
643 // Removing a node from a selection can cause widget updates.
644 document().nodeChildrenWillBeRemoved(*this);
645 }
646
647 // FIXME: Remove this NodeVector. Right now WebPluginContainerImpl::m_element is a
648 // raw ptr which means the code below can drop the last ref to a plugin element and
649 // then the code in UpdateSuspendScope::performDeferredWidgetTreeOperations will
650 // try to destroy the plugin which will be a use-after-free. We should use a RefPtr
651 // in the WebPluginContainerImpl instead.
652 NodeVector removedChildren;
653 {
654 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates;
655
656 {
657 EventDispatchForbiddenScope assertNoEventDispatch;
658 ScriptForbiddenScope forbidScript;
659
660 removedChildren.reserveInitialCapacity(countChildren());
661
662 while (RefPtrWillBeRawPtr<Node> child = m_firstChild) {
663 removeBetween(0, child->nextSibling(), *child);
664 removedChildren.append(child.get());
665 notifyNodeRemoved(*child);
666 }
667 }
668
669 ChildrenChange change = {AllChildrenRemoved, nullptr, nullptr, ChildrenChangeSourceAPI};
670 childrenChanged(change);
671 }
672
673 // We don't fire the DOMSubtreeModified event for Attr Nodes. This matches the behavior
674 // of IE and Firefox. This event is fired synchronously and is a source of trouble for
675 // attributes as the JS callback could alter the attributes and leave us in a bad state.
676 if (!isAttributeNode())
677 dispatchSubtreeModifiedEvent();
678 }
679
appendChild(PassRefPtrWillBeRawPtr<Node> newChild,ExceptionState & exceptionState)680 PassRefPtrWillBeRawPtr<Node> ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, ExceptionState& exceptionState)
681 {
682 RefPtrWillBeRawPtr<ContainerNode> protect(this);
683
684 #if !ENABLE(OILPAN)
685 // Check that this node is not "floating".
686 // If it is, it can be deleted as a side effect of sending mutation events.
687 ASSERT(refCount() || parentOrShadowHostNode());
688 #endif
689
690 // Make sure adding the new child is ok
691 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
692 if (exceptionState.hadException())
693 return nullptr;
694 return newChild;
695 }
696 ASSERT(newChild);
697
698 if (newChild == m_lastChild) // nothing to do
699 return newChild;
700
701 NodeVector targets;
702 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
703 if (exceptionState.hadException())
704 return nullptr;
705
706 if (targets.isEmpty())
707 return newChild;
708
709 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
710 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
711 if (exceptionState.hadException())
712 return nullptr;
713 return newChild;
714 }
715
716 InspectorInstrumentation::willInsertDOMNode(this);
717
718 // Now actually add the child(ren)
719 ChildListMutationScope mutation(*this);
720 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
721 ASSERT(*it);
722 Node& child = **it;
723
724 // If the child has a parent again, just stop what we're doing, because
725 // that means someone is doing something with DOM mutation -- can't re-parent
726 // a child that already has a parent.
727 if (child.parentNode())
728 break;
729
730 {
731 EventDispatchForbiddenScope assertNoEventDispatch;
732 ScriptForbiddenScope forbidScript;
733
734 treeScope().adoptIfNeeded(child);
735 appendChildCommon(child);
736 }
737
738 updateTreeAfterInsertion(child);
739 }
740
741 dispatchSubtreeModifiedEvent();
742 return newChild;
743 }
744
parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)745 void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)
746 {
747 ASSERT(newChild);
748 ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
749 ASSERT(!newChild->isDocumentFragment());
750 ASSERT(!isHTMLTemplateElement(this));
751
752 RefPtrWillBeRawPtr<Node> protect(this);
753
754 if (document() != newChild->document())
755 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
756
757 {
758 EventDispatchForbiddenScope assertNoEventDispatch;
759 ScriptForbiddenScope forbidScript;
760
761 treeScope().adoptIfNeeded(*newChild);
762 appendChildCommon(*newChild);
763 newChild->updateAncestorConnectedSubframeCountForInsertion();
764 ChildListMutationScope(*this).childAdded(*newChild);
765 }
766
767 notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
768 }
769
notifyNodeInserted(Node & root,ChildrenChangeSource source)770 void ContainerNode::notifyNodeInserted(Node& root, ChildrenChangeSource source)
771 {
772 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
773 ASSERT(!root.isShadowRoot());
774
775 InspectorInstrumentation::didInsertDOMNode(&root);
776
777 RefPtrWillBeRawPtr<Node> protect(this);
778 RefPtrWillBeRawPtr<Node> protectNode(root);
779
780 NodeVector postInsertionNotificationTargets;
781 notifyNodeInsertedInternal(root, postInsertionNotificationTargets);
782
783 childrenChanged(ChildrenChange::forInsertion(root, source));
784
785 for (size_t i = 0; i < postInsertionNotificationTargets.size(); ++i) {
786 Node* targetNode = postInsertionNotificationTargets[i].get();
787 if (targetNode->inDocument())
788 targetNode->didNotifySubtreeInsertionsToDocument();
789 }
790 }
791
notifyNodeInsertedInternal(Node & root,NodeVector & postInsertionNotificationTargets)792 void ContainerNode::notifyNodeInsertedInternal(Node& root, NodeVector& postInsertionNotificationTargets)
793 {
794 EventDispatchForbiddenScope assertNoEventDispatch;
795 ScriptForbiddenScope forbidScript;
796
797 for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
798 // As an optimization we don't notify leaf nodes when when inserting
799 // into detached subtrees.
800 if (!inDocument() && !node->isContainerNode())
801 continue;
802 if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node->insertedInto(this))
803 postInsertionNotificationTargets.append(node);
804 for (ShadowRoot* shadowRoot = node->youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot())
805 notifyNodeInsertedInternal(*shadowRoot, postInsertionNotificationTargets);
806 }
807 }
808
notifyNodeRemoved(Node & root)809 void ContainerNode::notifyNodeRemoved(Node& root)
810 {
811 ScriptForbiddenScope forbidScript;
812 EventDispatchForbiddenScope assertNoEventDispatch;
813
814 Document& document = root.document();
815 for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
816 // As an optimization we skip notifying Text nodes and other leaf nodes
817 // of removal when they're not in the Document tree since the virtual
818 // call to removedFrom is not needed.
819 if (!node->inDocument() && !node->isContainerNode())
820 continue;
821 if (document.cssTarget() == node)
822 document.setCSSTarget(nullptr);
823 node->removedFrom(this);
824 for (ShadowRoot* shadowRoot = node->youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot())
825 notifyNodeRemoved(*shadowRoot);
826 }
827 }
828
attach(const AttachContext & context)829 void ContainerNode::attach(const AttachContext& context)
830 {
831 attachChildren(context);
832 clearChildNeedsStyleRecalc();
833 Node::attach(context);
834 }
835
detach(const AttachContext & context)836 void ContainerNode::detach(const AttachContext& context)
837 {
838 detachChildren(context);
839 clearChildNeedsStyleRecalc();
840 Node::detach(context);
841 }
842
childrenChanged(const ChildrenChange & change)843 void ContainerNode::childrenChanged(const ChildrenChange& change)
844 {
845 document().incDOMTreeVersion();
846 if (!change.byParser && change.type != TextChanged)
847 document().updateRangesAfterChildrenChanged(this);
848 invalidateNodeListCachesInAncestors();
849 if (change.isChildInsertion() && !childNeedsStyleRecalc()) {
850 setChildNeedsStyleRecalc();
851 markAncestorsWithChildNeedsStyleRecalc();
852 }
853 }
854
cloneChildNodes(ContainerNode * clone)855 void ContainerNode::cloneChildNodes(ContainerNode *clone)
856 {
857 TrackExceptionState exceptionState;
858 for (Node* n = firstChild(); n && !exceptionState.hadException(); n = n->nextSibling())
859 clone->appendChild(n->cloneNode(true), exceptionState);
860 }
861
862
getUpperLeftCorner(FloatPoint & point) const863 bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const
864 {
865 if (!renderer())
866 return false;
867 // What is this code really trying to do?
868 RenderObject* o = renderer();
869
870 if (!o->isInline() || o->isReplaced()) {
871 point = o->localToAbsolute(FloatPoint(), UseTransforms);
872 return true;
873 }
874
875 // find the next text/image child, to get a position
876 while (o) {
877 RenderObject* p = o;
878 if (RenderObject* oFirstChild = o->slowFirstChild()) {
879 o = oFirstChild;
880 } else if (o->nextSibling()) {
881 o = o->nextSibling();
882 } else {
883 RenderObject* next = 0;
884 while (!next && o->parent()) {
885 o = o->parent();
886 next = o->nextSibling();
887 }
888 o = next;
889
890 if (!o)
891 break;
892 }
893 ASSERT(o);
894
895 if (!o->isInline() || o->isReplaced()) {
896 point = o->localToAbsolute(FloatPoint(), UseTransforms);
897 return true;
898 }
899
900 if (p->node() && p->node() == this && o->isText() && !o->isBR() && !toRenderText(o)->firstTextBox()) {
901 // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor
902 } else if ((o->isText() && !o->isBR()) || o->isReplaced()) {
903 point = FloatPoint();
904 if (o->isText() && toRenderText(o)->firstTextBox()) {
905 point.move(toRenderText(o)->linesBoundingBox().x(), toRenderText(o)->firstTextBox()->root().lineTop().toFloat());
906 } else if (o->isBox()) {
907 RenderBox* box = toRenderBox(o);
908 point.moveBy(box->location());
909 }
910 point = o->container()->localToAbsolute(point, UseTransforms);
911 return true;
912 }
913 }
914
915 // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
916 // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
917 if (!o && document().view()) {
918 point = FloatPoint(0, document().view()->contentsHeight());
919 return true;
920 }
921 return false;
922 }
923
getLowerRightCorner(FloatPoint & point) const924 bool ContainerNode::getLowerRightCorner(FloatPoint& point) const
925 {
926 if (!renderer())
927 return false;
928
929 RenderObject* o = renderer();
930 if (!o->isInline() || o->isReplaced()) {
931 RenderBox* box = toRenderBox(o);
932 point = o->localToAbsolute(LayoutPoint(box->size()), UseTransforms);
933 return true;
934 }
935
936 // find the last text/image child, to get a position
937 while (o) {
938 if (RenderObject* oLastChild = o->slowLastChild()) {
939 o = oLastChild;
940 } else if (o->previousSibling()) {
941 o = o->previousSibling();
942 } else {
943 RenderObject* prev = 0;
944 while (!prev) {
945 o = o->parent();
946 if (!o)
947 return false;
948 prev = o->previousSibling();
949 }
950 o = prev;
951 }
952 ASSERT(o);
953 if (o->isText() || o->isReplaced()) {
954 point = FloatPoint();
955 if (o->isText()) {
956 RenderText* text = toRenderText(o);
957 IntRect linesBox = text->linesBoundingBox();
958 if (!linesBox.maxX() && !linesBox.maxY())
959 continue;
960 point.moveBy(linesBox.maxXMaxYCorner());
961 } else {
962 RenderBox* box = toRenderBox(o);
963 point.moveBy(box->frameRect().maxXMaxYCorner());
964 }
965 point = o->container()->localToAbsolute(point, UseTransforms);
966 return true;
967 }
968 }
969 return true;
970 }
971
972 // FIXME: This override is only needed for inline anchors without an
973 // InlineBox and it does not belong in ContainerNode as it reaches into
974 // the render and line box trees.
975 // https://code.google.com/p/chromium/issues/detail?id=248354
boundingBox() const976 LayoutRect ContainerNode::boundingBox() const
977 {
978 FloatPoint upperLeft, lowerRight;
979 bool foundUpperLeft = getUpperLeftCorner(upperLeft);
980 bool foundLowerRight = getLowerRightCorner(lowerRight);
981
982 // If we've found one corner, but not the other,
983 // then we should just return a point at the corner that we did find.
984 if (foundUpperLeft != foundLowerRight) {
985 if (foundUpperLeft)
986 lowerRight = upperLeft;
987 else
988 upperLeft = lowerRight;
989 }
990
991 return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft));
992 }
993
994 // This is used by FrameSelection to denote when the active-state of the page has changed
995 // independent of the focused element changing.
focusStateChanged()996 void ContainerNode::focusStateChanged()
997 {
998 // If we're just changing the window's active state and the focused node has no
999 // renderer we can just ignore the state change.
1000 if (!renderer())
1001 return;
1002
1003 if (styleChangeType() < SubtreeStyleChange) {
1004 if (renderStyle()->affectedByFocus() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1005 setNeedsStyleRecalc(SubtreeStyleChange);
1006 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus())
1007 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoFocus, *toElement(this));
1008 else if (renderStyle()->affectedByFocus())
1009 setNeedsStyleRecalc(LocalStyleChange);
1010 }
1011
1012 if (renderer() && renderer()->style()->hasAppearance())
1013 RenderTheme::theme().stateChanged(renderer(), FocusControlState);
1014 }
1015
setFocus(bool received)1016 void ContainerNode::setFocus(bool received)
1017 {
1018 if (focused() == received)
1019 return;
1020
1021 Node::setFocus(received);
1022
1023 focusStateChanged();
1024
1025 if (renderer() || received)
1026 return;
1027
1028 // If :focus sets display: none, we lose focus but still need to recalc our style.
1029 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus() && styleChangeType() < SubtreeStyleChange)
1030 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoFocus, *toElement(this));
1031 else
1032 setNeedsStyleRecalc(LocalStyleChange);
1033 }
1034
setActive(bool down)1035 void ContainerNode::setActive(bool down)
1036 {
1037 if (down == active())
1038 return;
1039
1040 Node::setActive(down);
1041
1042 // FIXME: Why does this not need to handle the display: none transition like :hover does?
1043 if (renderer()) {
1044 if (styleChangeType() < SubtreeStyleChange) {
1045 if (renderStyle()->affectedByActive() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1046 setNeedsStyleRecalc(SubtreeStyleChange);
1047 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByActive())
1048 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoActive, *toElement(this));
1049 else if (renderStyle()->affectedByActive())
1050 setNeedsStyleRecalc(LocalStyleChange);
1051 }
1052
1053 if (renderStyle()->hasAppearance())
1054 RenderTheme::theme().stateChanged(renderer(), PressedControlState);
1055 }
1056 }
1057
setHovered(bool over)1058 void ContainerNode::setHovered(bool over)
1059 {
1060 if (over == hovered())
1061 return;
1062
1063 Node::setHovered(over);
1064
1065 // If :hover sets display: none we lose our hover but still need to recalc our style.
1066 if (!renderer()) {
1067 if (over)
1068 return;
1069 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover() && styleChangeType() < SubtreeStyleChange)
1070 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoHover, *toElement(this));
1071 else
1072 setNeedsStyleRecalc(LocalStyleChange);
1073 return;
1074 }
1075
1076 if (styleChangeType() < SubtreeStyleChange) {
1077 if (renderStyle()->affectedByHover() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1078 setNeedsStyleRecalc(SubtreeStyleChange);
1079 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover())
1080 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoHover, *toElement(this));
1081 else if (renderStyle()->affectedByHover())
1082 setNeedsStyleRecalc(LocalStyleChange);
1083 }
1084
1085 if (renderer()->style()->hasAppearance())
1086 RenderTheme::theme().stateChanged(renderer(), HoverControlState);
1087 }
1088
children()1089 PassRefPtrWillBeRawPtr<HTMLCollection> ContainerNode::children()
1090 {
1091 return ensureCachedCollection<HTMLCollection>(NodeChildren);
1092 }
1093
countChildren() const1094 unsigned ContainerNode::countChildren() const
1095 {
1096 unsigned count = 0;
1097 Node *n;
1098 for (n = firstChild(); n; n = n->nextSibling())
1099 count++;
1100 return count;
1101 }
1102
querySelector(const AtomicString & selectors,ExceptionState & exceptionState)1103 PassRefPtrWillBeRawPtr<Element> ContainerNode::querySelector(const AtomicString& selectors, ExceptionState& exceptionState)
1104 {
1105 if (selectors.isEmpty()) {
1106 exceptionState.throwDOMException(SyntaxError, "The provided selector is empty.");
1107 return nullptr;
1108 }
1109
1110 SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), exceptionState);
1111 if (!selectorQuery)
1112 return nullptr;
1113 return selectorQuery->queryFirst(*this);
1114 }
1115
querySelectorAll(const AtomicString & selectors,ExceptionState & exceptionState)1116 PassRefPtrWillBeRawPtr<StaticElementList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionState& exceptionState)
1117 {
1118 if (selectors.isEmpty()) {
1119 exceptionState.throwDOMException(SyntaxError, "The provided selector is empty.");
1120 return nullptr;
1121 }
1122
1123 SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), exceptionState);
1124 if (!selectorQuery)
1125 return nullptr;
1126
1127 return selectorQuery->queryAll(*this);
1128 }
1129
dispatchChildInsertionEvents(Node & child)1130 static void dispatchChildInsertionEvents(Node& child)
1131 {
1132 if (child.isInShadowTree())
1133 return;
1134
1135 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1136
1137 RefPtrWillBeRawPtr<Node> c(child);
1138 RefPtrWillBeRawPtr<Document> document(child.document());
1139
1140 if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
1141 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInserted, true, c->parentNode()));
1142
1143 // dispatch the DOMNodeInsertedIntoDocument event to all descendants
1144 if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
1145 for (; c; c = NodeTraversal::next(*c, &child))
1146 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInsertedIntoDocument, false));
1147 }
1148 }
1149
dispatchChildRemovalEvents(Node & child)1150 static void dispatchChildRemovalEvents(Node& child)
1151 {
1152 if (child.isInShadowTree()) {
1153 InspectorInstrumentation::willRemoveDOMNode(&child);
1154 return;
1155 }
1156
1157 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1158
1159 InspectorInstrumentation::willRemoveDOMNode(&child);
1160
1161 RefPtrWillBeRawPtr<Node> c(child);
1162 RefPtrWillBeRawPtr<Document> document(child.document());
1163
1164 // dispatch pre-removal mutation events
1165 if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) {
1166 NodeChildRemovalTracker scope(child);
1167 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemoved, true, c->parentNode()));
1168 }
1169
1170 // dispatch the DOMNodeRemovedFromDocument event to all descendants
1171 if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
1172 NodeChildRemovalTracker scope(child);
1173 for (; c; c = NodeTraversal::next(*c, &child))
1174 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemovedFromDocument, false));
1175 }
1176 }
1177
updateTreeAfterInsertion(Node & child)1178 void ContainerNode::updateTreeAfterInsertion(Node& child)
1179 {
1180 #if !ENABLE(OILPAN)
1181 ASSERT(refCount());
1182 ASSERT(child.refCount());
1183 #endif
1184
1185 ChildListMutationScope(*this).childAdded(child);
1186
1187 notifyNodeInserted(child);
1188
1189 dispatchChildInsertionEvents(child);
1190 }
1191
hasRestyleFlagInternal(DynamicRestyleFlags mask) const1192 bool ContainerNode::hasRestyleFlagInternal(DynamicRestyleFlags mask) const
1193 {
1194 return rareData()->hasRestyleFlag(mask);
1195 }
1196
hasRestyleFlagsInternal() const1197 bool ContainerNode::hasRestyleFlagsInternal() const
1198 {
1199 return rareData()->hasRestyleFlags();
1200 }
1201
setRestyleFlag(DynamicRestyleFlags mask)1202 void ContainerNode::setRestyleFlag(DynamicRestyleFlags mask)
1203 {
1204 ASSERT(isElementNode() || isShadowRoot());
1205 ensureRareData().setRestyleFlag(mask);
1206 }
1207
recalcChildStyle(StyleRecalcChange change)1208 void ContainerNode::recalcChildStyle(StyleRecalcChange change)
1209 {
1210 ASSERT(document().inStyleRecalc());
1211 ASSERT(change >= UpdatePseudoElements || childNeedsStyleRecalc());
1212 ASSERT(!needsStyleRecalc());
1213
1214 if (change < Force && hasRareData() && childNeedsStyleRecalc())
1215 checkForChildrenAdjacentRuleChanges();
1216
1217 // This loop is deliberately backwards because we use insertBefore in the rendering tree, and want to avoid
1218 // a potentially n^2 loop to find the insertion point while resolving style. Having us start from the last
1219 // child and work our way back means in the common case, we'll find the insertion point in O(1) time.
1220 // See crbug.com/288225
1221 StyleResolver& styleResolver = document().ensureStyleResolver();
1222 Text* lastTextNode = 0;
1223 for (Node* child = lastChild(); child; child = child->previousSibling()) {
1224 if (child->isTextNode()) {
1225 toText(child)->recalcTextStyle(change, lastTextNode);
1226 lastTextNode = toText(child);
1227 } else if (child->isElementNode()) {
1228 Element* element = toElement(child);
1229 if (element->shouldCallRecalcStyle(change))
1230 element->recalcStyle(change, lastTextNode);
1231 else if (element->supportsStyleSharing())
1232 styleResolver.addToStyleSharingList(*element);
1233 if (element->renderer())
1234 lastTextNode = 0;
1235 }
1236 }
1237 }
1238
checkForChildrenAdjacentRuleChanges()1239 void ContainerNode::checkForChildrenAdjacentRuleChanges()
1240 {
1241 bool hasDirectAdjacentRules = childrenAffectedByDirectAdjacentRules();
1242 bool hasIndirectAdjacentRules = childrenAffectedByIndirectAdjacentRules();
1243
1244 if (!hasDirectAdjacentRules && !hasIndirectAdjacentRules)
1245 return;
1246
1247 unsigned forceCheckOfNextElementCount = 0;
1248 bool forceCheckOfAnyElementSibling = false;
1249 Document& document = this->document();
1250
1251 for (Element* child = ElementTraversal::firstChild(*this); child; child = ElementTraversal::nextSibling(*child)) {
1252 bool childRulesChanged = child->needsStyleRecalc() && child->styleChangeType() >= SubtreeStyleChange;
1253
1254 if (forceCheckOfNextElementCount || forceCheckOfAnyElementSibling)
1255 child->setNeedsStyleRecalc(SubtreeStyleChange);
1256
1257 if (childRulesChanged && hasDirectAdjacentRules)
1258 forceCheckOfNextElementCount = document.styleEngine()->maxDirectAdjacentSelectors();
1259 else if (forceCheckOfNextElementCount)
1260 --forceCheckOfNextElementCount;
1261
1262 forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules);
1263 }
1264 }
1265
checkForSiblingStyleChanges(SiblingCheckType changeType,Node * nodeBeforeChange,Node * nodeAfterChange)1266 void ContainerNode::checkForSiblingStyleChanges(SiblingCheckType changeType, Node* nodeBeforeChange, Node* nodeAfterChange)
1267 {
1268 if (!inActiveDocument() || document().hasPendingForcedStyleRecalc() || styleChangeType() >= SubtreeStyleChange)
1269 return;
1270
1271 if (needsStyleRecalc() && childrenAffectedByPositionalRules())
1272 return;
1273
1274 // Forward positional selectors include nth-child, nth-of-type, first-of-type and only-of-type.
1275 // The indirect adjacent selector is the ~ selector.
1276 // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type.
1277 // We have to invalidate everything following the insertion point in the forward and indirect adjacent case,
1278 // and everything before the insertion point in the backward case.
1279 // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to.
1280 // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids
1281 // here. recalcStyle will then force a walk of the children when it sees that this has happened.
1282 if (((childrenAffectedByForwardPositionalRules() || childrenAffectedByIndirectAdjacentRules()) && nodeAfterChange)
1283 || (childrenAffectedByBackwardPositionalRules() && nodeBeforeChange)) {
1284 setNeedsStyleRecalc(SubtreeStyleChange);
1285 return;
1286 }
1287
1288 // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1289 // In the DOM case, we only need to do something if |afterChange| is not 0.
1290 // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
1291 if (childrenAffectedByFirstChildRules() && nodeAfterChange) {
1292 ASSERT(changeType != FinishedParsingChildren);
1293 // Find our new first child element.
1294 Element* firstChildElement = ElementTraversal::firstChild(*this);
1295 RenderStyle* firstChildElementStyle = firstChildElement ? firstChildElement->renderStyle() : 0;
1296
1297 // Find the first element after the change.
1298 Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange);
1299 RenderStyle* elementAfterChangeStyle = elementAfterChange ? elementAfterChange->renderStyle() : 0;
1300
1301 // This is the element insertion as first child element case.
1302 if (firstChildElement != elementAfterChange && elementAfterChangeStyle && elementAfterChangeStyle->firstChildState()) {
1303 ASSERT(changeType == SiblingElementInserted);
1304 elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange);
1305 }
1306
1307 // This is the first child element removal case.
1308 if (changeType == SiblingElementRemoved && firstChildElement == elementAfterChange && firstChildElement && (!firstChildElementStyle || !firstChildElementStyle->firstChildState()))
1309 firstChildElement->setNeedsStyleRecalc(SubtreeStyleChange);
1310 }
1311
1312 // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1313 // In the DOM case, we only need to do something if |afterChange| is not 0.
1314 if (childrenAffectedByLastChildRules() && nodeBeforeChange) {
1315 // Find our new last child element.
1316 Element* lastChildElement = ElementTraversal::lastChild(*this);
1317 RenderStyle* lastChildElementStyle = lastChildElement ? lastChildElement->renderStyle() : 0;
1318
1319 // Find the last element before the change.
1320 Element* elementBeforeChange = nodeBeforeChange->isElementNode() ? toElement(nodeBeforeChange) : ElementTraversal::previousSibling(*nodeBeforeChange);
1321 RenderStyle* elementBeforeChangeStyle = elementBeforeChange ? elementBeforeChange->renderStyle() : 0;
1322
1323 // This is the element insertion as last child element case.
1324 if (lastChildElement != elementBeforeChange && elementBeforeChangeStyle && elementBeforeChangeStyle->lastChildState()) {
1325 ASSERT(SiblingElementInserted);
1326 elementBeforeChange->setNeedsStyleRecalc(SubtreeStyleChange);
1327 }
1328
1329 // This is the last child element removal case. The parser callback case is similar to node removal as well in that we need to change the last child
1330 // to match now.
1331 if ((changeType == SiblingElementRemoved || changeType == FinishedParsingChildren) && lastChildElement == elementBeforeChange && lastChildElement && (!lastChildElementStyle || !lastChildElementStyle->lastChildState()))
1332 lastChildElement->setNeedsStyleRecalc(SubtreeStyleChange);
1333 }
1334
1335 // The + selector. We need to invalidate the first element following the change. It is the only possible element
1336 // that could be affected by this DOM change.
1337 if (childrenAffectedByDirectAdjacentRules() && nodeAfterChange) {
1338 if (Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange))
1339 elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange);
1340 }
1341 }
1342
invalidateNodeListCachesInAncestors(const QualifiedName * attrName,Element * attributeOwnerElement)1343 void ContainerNode::invalidateNodeListCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
1344 {
1345 if (hasRareData() && (!attrName || isAttributeNode())) {
1346 if (NodeListsNodeData* lists = rareData()->nodeLists()) {
1347 if (ChildNodeList* childNodeList = lists->childNodeList(*this))
1348 childNodeList->invalidateCache();
1349 }
1350 }
1351
1352 // Modifications to attributes that are not associated with an Element can't invalidate NodeList caches.
1353 if (attrName && !attributeOwnerElement)
1354 return;
1355
1356 if (!document().shouldInvalidateNodeListCaches(attrName))
1357 return;
1358
1359 document().invalidateNodeListCaches(attrName);
1360
1361 for (ContainerNode* node = this; node; node = node->parentNode()) {
1362 if (NodeListsNodeData* lists = node->nodeLists())
1363 lists->invalidateCaches(attrName);
1364 }
1365 }
1366
getElementsByTagName(const AtomicString & localName)1367 PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagName(const AtomicString& localName)
1368 {
1369 if (localName.isNull())
1370 return nullptr;
1371
1372 if (document().isHTMLDocument())
1373 return ensureCachedCollection<HTMLTagCollection>(HTMLTagCollectionType, localName);
1374 return ensureCachedCollection<TagCollection>(TagCollectionType, localName);
1375 }
1376
getElementsByTagNameNS(const AtomicString & namespaceURI,const AtomicString & localName)1377 PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
1378 {
1379 if (localName.isNull())
1380 return nullptr;
1381
1382 if (namespaceURI == starAtom)
1383 return getElementsByTagName(localName);
1384
1385 return ensureCachedCollection<TagCollection>(TagCollectionType, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
1386 }
1387
1388 // Takes an AtomicString in argument because it is common for elements to share the same name attribute.
1389 // Therefore, the NameNodeList factory function expects an AtomicString type.
getElementsByName(const AtomicString & elementName)1390 PassRefPtrWillBeRawPtr<NameNodeList> ContainerNode::getElementsByName(const AtomicString& elementName)
1391 {
1392 return ensureCachedCollection<NameNodeList>(NameNodeListType, elementName);
1393 }
1394
1395 // Takes an AtomicString in argument because it is common for elements to share the same set of class names.
1396 // Therefore, the ClassNodeList factory function expects an AtomicString type.
getElementsByClassName(const AtomicString & classNames)1397 PassRefPtrWillBeRawPtr<ClassCollection> ContainerNode::getElementsByClassName(const AtomicString& classNames)
1398 {
1399 return ensureCachedCollection<ClassCollection>(ClassCollectionType, classNames);
1400 }
1401
radioNodeList(const AtomicString & name,bool onlyMatchImgElements)1402 PassRefPtrWillBeRawPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name, bool onlyMatchImgElements)
1403 {
1404 ASSERT(isHTMLFormElement(this) || isHTMLFieldSetElement(this));
1405 CollectionType type = onlyMatchImgElements ? RadioImgNodeListType : RadioNodeListType;
1406 return ensureCachedCollection<RadioNodeList>(type, name);
1407 }
1408
getElementById(const AtomicString & id) const1409 Element* ContainerNode::getElementById(const AtomicString& id) const
1410 {
1411 if (isInTreeScope()) {
1412 // Fast path if we are in a tree scope: call getElementById() on tree scope
1413 // and check if the matching element is in our subtree.
1414 Element* element = treeScope().getElementById(id);
1415 if (!element)
1416 return 0;
1417 if (element->isDescendantOf(this))
1418 return element;
1419 }
1420
1421 // Fall back to traversing our subtree. In case of duplicate ids, the first element found will be returned.
1422 for (Element* element = ElementTraversal::firstWithin(*this); element; element = ElementTraversal::next(*element, this)) {
1423 if (element->getIdAttribute() == id)
1424 return element;
1425 }
1426 return 0;
1427 }
1428
ensureNodeLists()1429 NodeListsNodeData& ContainerNode::ensureNodeLists()
1430 {
1431 return ensureRareData().ensureNodeLists();
1432 }
1433
1434 #if ENABLE(ASSERT)
childAttachedAllowedWhenAttachingChildren(ContainerNode * node)1435 bool childAttachedAllowedWhenAttachingChildren(ContainerNode* node)
1436 {
1437 if (node->isShadowRoot())
1438 return true;
1439
1440 if (node->isInsertionPoint())
1441 return true;
1442
1443 if (node->isElementNode() && toElement(node)->shadow())
1444 return true;
1445
1446 return false;
1447 }
1448 #endif
1449
1450 } // namespace blink
1451