summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/ContainerNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/ContainerNode.cpp')
-rw-r--r--Source/WebCore/dom/ContainerNode.cpp1125
1 files changed, 485 insertions, 640 deletions
diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp
index 44a4c3a14..e9ed30dd7 100644
--- a/Source/WebCore/dom/ContainerNode.cpp
+++ b/Source/WebCore/dom/ContainerNode.cpp
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2016 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -24,71 +24,71 @@
#include "ContainerNode.h"
#include "AXObjectCache.h"
+#include "AllDescendantsCollection.h"
#include "ChildListMutationScope.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
-#include "ClassNodeList.h"
+#include "ClassCollection.h"
+#include "CommonVM.h"
#include "ContainerNodeAlgorithms.h"
#include "Editor.h"
+#include "EventNames.h"
#include "FloatRect.h"
#include "FrameView.h"
+#include "GenericCachedHTMLCollection.h"
+#include "HTMLFormControlsCollection.h"
+#include "HTMLOptionsCollection.h"
+#include "HTMLSlotElement.h"
+#include "HTMLTableRowsCollection.h"
#include "InlineTextBox.h"
-#include "InsertionPoint.h"
-#include "JSLazyEventListener.h"
+#include "InspectorInstrumentation.h"
#include "JSNode.h"
#include "LabelsNodeList.h"
-#include "LoaderStrategy.h"
-#include "MemoryCache.h"
#include "MutationEvent.h"
#include "NameNodeList.h"
+#include "NoEventDispatchAssertion.h"
#include "NodeRareData.h"
#include "NodeRenderStyle.h"
-#include "PlatformStrategies.h"
#include "RadioNodeList.h"
#include "RenderBox.h"
#include "RenderTheme.h"
+#include "RenderTreeUpdater.h"
#include "RenderWidget.h"
-#include "ResourceLoadScheduler.h"
#include "RootInlineBox.h"
+#include "RuntimeEnabledFeatures.h"
+#include "SVGDocumentExtensions.h"
+#include "SVGElement.h"
+#include "SVGNames.h"
+#include "SVGUseElement.h"
#include "SelectorQuery.h"
#include "TemplateContentDocumentFragment.h"
-#include <wtf/CurrentTime.h>
-
-#if ENABLE(DELETION_UI)
-#include "DeleteButtonController.h"
-#endif
+#include <algorithm>
+#include <wtf/Variant.h>
namespace WebCore {
static void dispatchChildInsertionEvents(Node&);
static void dispatchChildRemovalEvents(Node&);
-typedef std::pair<RefPtr<Node>, unsigned> CallbackParameters;
-typedef std::pair<NodeCallback, CallbackParameters> CallbackInfo;
-typedef Vector<CallbackInfo> NodeCallbackQueue;
-
-static NodeCallbackQueue* s_postAttachCallbackQueue;
-
-static size_t s_attachDepth;
-static bool s_shouldReEnableMemoryCacheCallsAfterAttach;
+ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot;
-ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot = 0;
-
-#ifndef NDEBUG
+#if !ASSERT_DISABLED
unsigned NoEventDispatchAssertion::s_count = 0;
+unsigned NoEventDispatchAssertion::DisableAssertionsInScope::s_existingCount = 0;
+NoEventDispatchAssertion::EventAllowedScope* NoEventDispatchAssertion::EventAllowedScope::s_currentScope = nullptr;
#endif
-static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionCode& ec)
+static ExceptionOr<void> collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes)
{
- if (!node.isDocumentFragment()) {
+ if (!is<DocumentFragment>(node)) {
nodes.append(node);
- if (ContainerNode* oldParent = node.parentNode())
- oldParent->removeChild(&node, ec);
- return;
+ auto* oldParent = node.parentNode();
+ if (!oldParent)
+ return { };
+ return oldParent->removeChild(node);
}
getChildNodes(node, nodes);
- toContainerNode(node).removeChildren();
+ downcast<DocumentFragment>(node).removeChildren();
+ return { };
}
// FIXME: This function must get a new name.
@@ -101,18 +101,21 @@ void ContainerNode::removeDetachedChildren()
child->updateAncestorConnectedSubframeCountForRemoval();
}
// FIXME: We should be able to ASSERT(!attached()) here: https://bugs.webkit.org/show_bug.cgi?id=107801
- removeDetachedChildrenInContainer<Node, ContainerNode>(*this);
+ removeDetachedChildrenInContainer(*this);
}
static inline void destroyRenderTreeIfNeeded(Node& child)
{
+ bool isElement = is<Element>(child);
+ auto hasDisplayContents = isElement && downcast<Element>(child).hasDisplayContents();
+ auto isNamedFlowElement = isElement && downcast<Element>(child).isNamedFlowContentElement();
// FIXME: Get rid of the named flow test.
- if (!child.renderer() && !child.inNamedFlow())
+ if (!child.renderer() && !hasDisplayContents && !isNamedFlowElement)
return;
- if (child.isElementNode())
- Style::detachRenderTree(toElement(child));
- else if (child.isTextNode())
- Style::detachTextRenderer(toText(child));
+ if (isElement)
+ RenderTreeUpdater::tearDownRenderers(downcast<Element>(child));
+ else if (is<Text>(child))
+ RenderTreeUpdater::tearDownRenderer(downcast<Text>(child));
}
void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent)
@@ -124,44 +127,48 @@ void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent)
if (oldParent->document().hasMutationObserversOfType(MutationObserver::ChildList)) {
ChildListMutationScope mutation(*oldParent);
- for (unsigned i = 0; i < children.size(); ++i)
- mutation.willRemoveChild(children[i].get());
+ for (auto& child : children)
+ mutation.willRemoveChild(child);
}
- // FIXME: We need to do notifyMutationObserversNodeWillDetach() for each child,
- // probably inside removeDetachedChildrenInContainer.
-
- oldParent->removeDetachedChildren();
+ disconnectSubframesIfNeeded(*oldParent, DescendantsOnly);
+ {
+ NoEventDispatchAssertion assertNoEventDispatch;
- for (unsigned i = 0; i < children.size(); ++i) {
- Node& child = children[i].get();
+ oldParent->document().nodeChildrenWillBeRemoved(*oldParent);
- destroyRenderTreeIfNeeded(child);
+ WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
+ while (RefPtr<Node> child = oldParent->m_firstChild) {
+ oldParent->removeBetween(nullptr, child->nextSibling(), *child);
+ notifyChildNodeRemoved(*oldParent, *child);
+ }
+ ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceParser };
+ childrenChanged(change);
+ }
- // FIXME: We need a no mutation event version of adoptNode.
- RefPtr<Node> adoptedChild = document().adoptNode(&children[i].get(), ASSERT_NO_EXCEPTION);
- parserAppendChild(adoptedChild.get());
- // FIXME: Together with adoptNode above, the tree scope might get updated recursively twice
- // (if the document changed or oldParent was in a shadow tree, AND *this is in a shadow tree).
- // Can we do better?
- treeScope().adoptIfNeeded(adoptedChild.get());
+ // FIXME: assert that we don't dispatch events here since this container node is still disconnected.
+ for (auto& child : children) {
+ RELEASE_ASSERT(!child->parentNode() && &child->treeScope() == &treeScope());
+ ASSERT(!ensurePreInsertionValidity(child, nullptr).hasException());
+ treeScope().adoptIfNeeded(child);
+ parserAppendChild(child);
}
}
ContainerNode::~ContainerNode()
{
- if (Document* document = documentInternal())
- willBeDeletedFrom(document);
+ if (!isDocumentNode())
+ willBeDeletedFrom(document());
removeDetachedChildren();
}
-static inline bool isChildTypeAllowed(ContainerNode* newParent, Node* child)
+static inline bool isChildTypeAllowed(ContainerNode& newParent, Node& child)
{
- if (!child->isDocumentFragment())
- return newParent->childTypeAllowed(child->nodeType());
+ if (!child.isDocumentFragment())
+ return newParent.childTypeAllowed(child.nodeType());
- for (Node* node = child->firstChild(); node; node = node->nextSibling()) {
- if (!newParent->childTypeAllowed(node->nodeType()))
+ for (Node* node = child.firstChild(); node; node = node->nextSibling()) {
+ if (!newParent.childTypeAllowed(node->nodeType()))
return false;
}
return true;
@@ -169,152 +176,129 @@ static inline bool isChildTypeAllowed(ContainerNode* newParent, Node* child)
static inline bool isInTemplateContent(const Node* node)
{
-#if ENABLE(TEMPLATE_ELEMENT)
Document& document = node->document();
return &document == document.templateDocument();
-#else
- UNUSED_PARAM(node);
- return false;
-#endif
}
-static inline bool containsConsideringHostElements(const Node* newChild, const Node* newParent)
+static inline bool containsConsideringHostElements(const Node& newChild, const Node& newParent)
{
- return (newParent->isInShadowTree() || isInTemplateContent(newParent))
- ? newChild->containsIncludingHostElements(newParent)
- : newChild->contains(newParent);
+ return (newParent.isInShadowTree() || isInTemplateContent(&newParent))
+ ? newChild.containsIncludingHostElements(&newParent)
+ : newChild.contains(&newParent);
}
-static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* newChild, Node* oldChild)
+static inline ExceptionOr<void> checkAcceptChild(ContainerNode& newParent, Node& newChild, const Node* refChild, Document::AcceptChildOperation operation)
{
- // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null
- if (!newChild)
- return NOT_FOUND_ERR;
-
// Use common case fast path if possible.
- if ((newChild->isElementNode() || newChild->isTextNode()) && newParent->isElementNode()) {
- ASSERT(!newParent->isReadOnlyNode());
- ASSERT(!newParent->isDocumentTypeNode());
+ if ((newChild.isElementNode() || newChild.isTextNode()) && newParent.isElementNode()) {
+ ASSERT(!newParent.isDocumentTypeNode());
ASSERT(isChildTypeAllowed(newParent, newChild));
if (containsConsideringHostElements(newChild, newParent))
- return HIERARCHY_REQUEST_ERR;
- return 0;
+ return Exception { HIERARCHY_REQUEST_ERR };
+ if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent)
+ return Exception { NOT_FOUND_ERR };
+ return { };
}
// This should never happen, but also protect release builds from tree corruption.
- ASSERT(!newChild->isPseudoElement());
- if (newChild->isPseudoElement())
- return HIERARCHY_REQUEST_ERR;
+ ASSERT(!newChild.isPseudoElement());
+ if (newChild.isPseudoElement())
+ return Exception { HIERARCHY_REQUEST_ERR };
- if (newParent->isReadOnlyNode())
- return NO_MODIFICATION_ALLOWED_ERR;
if (containsConsideringHostElements(newChild, newParent))
- return HIERARCHY_REQUEST_ERR;
+ return Exception { HIERARCHY_REQUEST_ERR };
+
+ if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent)
+ return Exception { NOT_FOUND_ERR };
- if (oldChild && newParent->isDocumentNode()) {
- if (!toDocument(newParent)->canReplaceChild(newChild, oldChild))
- return HIERARCHY_REQUEST_ERR;
+ if (is<Document>(newParent)) {
+ if (!downcast<Document>(newParent).canAcceptChild(newChild, refChild, operation))
+ return Exception { HIERARCHY_REQUEST_ERR };
} else if (!isChildTypeAllowed(newParent, newChild))
- return HIERARCHY_REQUEST_ERR;
+ return Exception { HIERARCHY_REQUEST_ERR };
- return 0;
+ return { };
}
-static inline bool checkAcceptChildGuaranteedNodeTypes(ContainerNode* newParent, Node* newChild, ExceptionCode& ec)
+static inline ExceptionOr<void> checkAcceptChildGuaranteedNodeTypes(ContainerNode& newParent, Node& newChild)
{
- ASSERT(!newParent->isReadOnlyNode());
- ASSERT(!newParent->isDocumentTypeNode());
+ ASSERT(!newParent.isDocumentTypeNode());
ASSERT(isChildTypeAllowed(newParent, newChild));
- if (newChild->contains(newParent)) {
- ec = HIERARCHY_REQUEST_ERR;
- return false;
- }
-
- return true;
+ if (newChild.contains(&newParent))
+ return Exception { HIERARCHY_REQUEST_ERR };
+ return { };
}
-static inline bool checkAddChild(ContainerNode* newParent, Node* newChild, ExceptionCode& ec)
+// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
+ExceptionOr<void> ContainerNode::ensurePreInsertionValidity(Node& newChild, Node* refChild)
{
- ec = checkAcceptChild(newParent, newChild, 0);
- if (ec)
- return false;
-
- return true;
+ return checkAcceptChild(*this, newChild, refChild, Document::AcceptChildOperation::InsertOrAdd);
}
-static inline bool checkReplaceChild(ContainerNode* newParent, Node* newChild, Node* oldChild, ExceptionCode& ec)
+// https://dom.spec.whatwg.org/#concept-node-replace
+static inline ExceptionOr<void> checkPreReplacementValidity(ContainerNode& newParent, Node& newChild, Node& oldChild)
{
- ec = checkAcceptChild(newParent, newChild, oldChild);
- if (ec)
- return false;
-
- return true;
+ return checkAcceptChild(newParent, newChild, &oldChild, Document::AcceptChildOperation::Replace);
}
-bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
+ExceptionOr<void> ContainerNode::insertBefore(Node& newChild, Node* refChild)
{
// Check that this node is not "floating".
// If it is, it can be deleted as a side effect of sending mutation events.
ASSERT(refCount() || parentOrShadowHostNode());
- Ref<ContainerNode> protect(*this);
-
- ec = 0;
-
- // insertBefore(node, 0) is equivalent to appendChild(node)
- if (!refChild)
- return appendChild(newChild, ec);
-
// Make sure adding the new child is OK.
- if (!checkAddChild(this, newChild.get(), ec))
- return false;
+ auto validityCheckResult = ensurePreInsertionValidity(newChild, refChild);
+ if (validityCheckResult.hasException())
+ return validityCheckResult.releaseException();
- // NOT_FOUND_ERR: Raised if refChild is not a child of this node
- if (refChild->parentNode() != this) {
- ec = NOT_FOUND_ERR;
- return false;
- }
+ if (refChild == &newChild)
+ refChild = newChild.nextSibling();
- if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do
- return true;
+ // insertBefore(node, null) is equivalent to appendChild(node)
+ if (!refChild)
+ return appendChildWithoutPreInsertionValidityCheck(newChild);
+ Ref<ContainerNode> protectedThis(*this);
Ref<Node> next(*refChild);
NodeVector targets;
- collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec);
- if (ec)
- return false;
+ auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
+ if (removeResult.hasException())
+ return removeResult.releaseException();
if (targets.isEmpty())
- return true;
+ return { };
// We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
- if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec))
- return false;
+ auto checkAcceptResult = checkAcceptChildGuaranteedNodeTypes(*this, newChild);
+ if (checkAcceptResult.hasException())
+ return checkAcceptResult.releaseException();
- InspectorInstrumentation::willInsertDOMNode(&document(), this);
+ InspectorInstrumentation::willInsertDOMNode(document(), *this);
ChildListMutationScope mutation(*this);
- for (auto it = targets.begin(), end = targets.end(); it != end; ++it) {
- Node& child = it->get();
-
+ for (auto& child : targets) {
// Due to arbitrary code running in response to a DOM mutation event it's
// possible that "next" is no longer a child of "this".
// It's also possible that "child" has been inserted elsewhere.
// In either of those cases, we'll just stop.
if (next->parentNode() != this)
break;
- if (child.parentNode())
+ if (child->parentNode())
break;
- treeScope().adoptIfNeeded(&child);
+ {
+ NoEventDispatchAssertion assertNoEventDispatch;
- insertBeforeCommon(next.get(), child);
+ treeScope().adoptIfNeeded(child);
+ insertBeforeCommon(next, child);
+ }
updateTreeAfterInsertion(child);
}
dispatchSubtreeModifiedEvent();
- return true;
+ return { };
}
void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
@@ -342,145 +326,161 @@ void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
newChild.setNextSibling(&nextChild);
}
-void ContainerNode::notifyChildInserted(Node& child, ChildChangeSource source)
+void ContainerNode::appendChildCommon(Node& child)
{
- ChildChange change;
- change.type = child.isElementNode() ? ElementInserted : child.isTextNode() ? TextInserted : NonContentsChildChanged;
- change.previousSiblingElement = ElementTraversal::previousSibling(&child);
- change.nextSiblingElement = ElementTraversal::nextSibling(&child);
- change.source = source;
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ child.setParentNode(this);
+
+ if (m_lastChild) {
+ child.setPreviousSibling(m_lastChild);
+ m_lastChild->setNextSibling(&child);
+ } else
+ m_firstChild = &child;
+
+ m_lastChild = &child;
+}
+
+inline auto ContainerNode::changeForChildInsertion(Node& child, ChildChangeSource source, ReplacedAllChildren replacedAllChildren) -> ChildChange
+{
+ if (replacedAllChildren == ReplacedAllChildren::Yes)
+ return { AllChildrenReplaced, nullptr, nullptr, source };
+
+ return {
+ child.isElementNode() ? ElementInserted : child.isTextNode() ? TextInserted : NonContentsChildInserted,
+ ElementTraversal::previousSibling(child),
+ ElementTraversal::nextSibling(child),
+ source
+ };
+}
+
+void ContainerNode::notifyChildInserted(Node& child, const ChildChange& change)
+{
+ ChildListMutationScope(*this).childAdded(child);
+
+ NodeVector postInsertionNotificationTargets;
+ notifyChildNodeInserted(*this, child, postInsertionNotificationTargets);
childrenChanged(change);
+
+ for (auto& target : postInsertionNotificationTargets)
+ target->finishedInsertingSubtree();
}
void ContainerNode::notifyChildRemoved(Node& child, Node* previousSibling, Node* nextSibling, ChildChangeSource source)
{
+ NoEventDispatchAssertion assertNoEventDispatch;
+ notifyChildNodeRemoved(*this, child);
+
ChildChange change;
- change.type = child.isElementNode() ? ElementRemoved : child.isTextNode() ? TextRemoved : NonContentsChildChanged;
- change.previousSiblingElement = (!previousSibling || previousSibling->isElementNode()) ? toElement(previousSibling) : ElementTraversal::previousSibling(previousSibling);
- change.nextSiblingElement = (!nextSibling || nextSibling->isElementNode()) ? toElement(nextSibling) : ElementTraversal::nextSibling(nextSibling);
+ change.type = is<Element>(child) ? ElementRemoved : is<Text>(child) ? TextRemoved : NonContentsChildRemoved;
+ change.previousSiblingElement = (!previousSibling || is<Element>(*previousSibling)) ? downcast<Element>(previousSibling) : ElementTraversal::previousSibling(*previousSibling);
+ change.nextSiblingElement = (!nextSibling || is<Element>(*nextSibling)) ? downcast<Element>(nextSibling) : ElementTraversal::nextSibling(*nextSibling);
change.source = source;
childrenChanged(change);
}
-void ContainerNode::parserInsertBefore(PassRefPtr<Node> newChild, Node* nextChild)
+void ContainerNode::parserInsertBefore(Node& newChild, Node& nextChild)
{
- ASSERT(newChild);
- ASSERT(nextChild);
- ASSERT(nextChild->parentNode() == this);
- ASSERT(!newChild->isDocumentFragment());
-#if ENABLE(TEMPLATE_ELEMENT)
+ ASSERT(nextChild.parentNode() == this);
+ ASSERT(!newChild.isDocumentFragment());
ASSERT(!hasTagName(HTMLNames::templateTag));
-#endif
- if (nextChild->previousSibling() == newChild || nextChild == newChild) // nothing to do
+ if (nextChild.previousSibling() == &newChild || &nextChild == &newChild) // nothing to do
return;
- if (&document() != &newChild->document())
- document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
-
- insertBeforeCommon(*nextChild, *newChild.get());
+ if (&document() != &newChild.document())
+ document().adoptNode(newChild);
- newChild->updateAncestorConnectedSubframeCountForInsertion();
+ insertBeforeCommon(nextChild, newChild);
- ChildListMutationScope(*this).childAdded(*newChild);
+ newChild.updateAncestorConnectedSubframeCountForInsertion();
- notifyChildInserted(*newChild, ChildChangeSourceParser);
-
- ChildNodeInsertionNotifier(*this).notify(*newChild);
-
- newChild->setNeedsStyleRecalc(ReconstructRenderTree);
+ notifyChildInserted(newChild, changeForChildInsertion(newChild, ChildChangeSourceParser));
}
-bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec)
+ExceptionOr<void> ContainerNode::replaceChild(Node& newChild, Node& oldChild)
{
// Check that this node is not "floating".
// If it is, it can be deleted as a side effect of sending mutation events.
ASSERT(refCount() || parentOrShadowHostNode());
- Ref<ContainerNode> protect(*this);
-
- ec = 0;
-
- if (oldChild == newChild) // nothing to do
- return true;
-
- if (!oldChild) {
- ec = NOT_FOUND_ERR;
- return false;
- }
+ Ref<ContainerNode> protectedThis(*this);
// Make sure replacing the old child with the new is ok
- if (!checkReplaceChild(this, newChild.get(), oldChild, ec))
- return false;
+ auto validityResult = checkPreReplacementValidity(*this, newChild, oldChild);
+ if (validityResult.hasException())
+ return validityResult.releaseException();
// NOT_FOUND_ERR: Raised if oldChild is not a child of this node.
- if (oldChild->parentNode() != this) {
- ec = NOT_FOUND_ERR;
- return false;
- }
+ if (oldChild.parentNode() != this)
+ return Exception { NOT_FOUND_ERR };
- ChildListMutationScope mutation(*this);
-
- RefPtr<Node> next = oldChild->nextSibling();
+ RefPtr<Node> refChild = oldChild.nextSibling();
+ if (refChild.get() == &newChild)
+ refChild = refChild->nextSibling();
- // Remove the node we're replacing
- Ref<Node> removedChild(*oldChild);
- removeChild(oldChild, ec);
- if (ec)
- return false;
+ NodeVector targets;
+ {
+ ChildListMutationScope mutation(*this);
+ auto collectResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
+ if (collectResult.hasException())
+ return collectResult.releaseException();
+ }
- if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
- return true;
+ // Do this one more time because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
+ validityResult = checkPreReplacementValidity(*this, newChild, oldChild);
+ if (validityResult.hasException())
+ return validityResult.releaseException();
- // Does this one more time because removeChild() fires a MutationEvent.
- if (!checkReplaceChild(this, newChild.get(), oldChild, ec))
- return false;
+ // Remove the node we're replacing.
+ Ref<Node> protectOldChild(oldChild);
- NodeVector targets;
- collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec);
- if (ec)
- return false;
+ ChildListMutationScope mutation(*this);
- // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
- if (!checkReplaceChild(this, newChild.get(), oldChild, ec))
- return false;
+ // If oldChild == newChild then oldChild no longer has a parent at this point.
+ if (oldChild.parentNode()) {
+ auto removeResult = removeChild(oldChild);
+ if (removeResult.hasException())
+ return removeResult.releaseException();
- InspectorInstrumentation::willInsertDOMNode(&document(), this);
+ // Does this one more time because removeChild() fires a MutationEvent.
+ validityResult = checkPreReplacementValidity(*this, newChild, oldChild);
+ if (validityResult.hasException())
+ return validityResult.releaseException();
+ }
- // Add the new child(ren)
- for (auto it = targets.begin(), end = targets.end(); it != end; ++it) {
- Node& child = it->get();
+ InspectorInstrumentation::willInsertDOMNode(document(), *this);
+ // Add the new child(ren).
+ for (auto& child : targets) {
// Due to arbitrary code running in response to a DOM mutation event it's
- // possible that "next" is no longer a child of "this".
+ // possible that "refChild" is no longer a child of "this".
// It's also possible that "child" has been inserted elsewhere.
// In either of those cases, we'll just stop.
- if (next && next->parentNode() != this)
+ if (refChild && refChild->parentNode() != this)
break;
- if (child.parentNode())
+ if (child->parentNode())
break;
- treeScope().adoptIfNeeded(&child);
-
- // Add child before "next".
{
NoEventDispatchAssertion assertNoEventDispatch;
- if (next)
- insertBeforeCommon(*next, child);
+ treeScope().adoptIfNeeded(child);
+ if (refChild)
+ insertBeforeCommon(*refChild, child.get());
else
- appendChildToContainer(&child, *this);
+ appendChildCommon(child);
}
- updateTreeAfterInsertion(child);
+ updateTreeAfterInsertion(child.get());
}
dispatchSubtreeModifiedEvent();
- return true;
+ return { };
}
-void ContainerNode::willRemoveChild(Node& child)
+static void willRemoveChild(ContainerNode& container, Node& child)
{
ASSERT(child.parentNode());
@@ -488,12 +488,11 @@ void ContainerNode::willRemoveChild(Node& child)
child.notifyMutationObserversNodeWillDetach();
dispatchChildRemovalEvents(child);
- if (child.parentNode() != this)
+ if (child.parentNode() != &container)
return;
- child.document().nodeWillBeRemoved(&child); // e.g. mutation event listener can create a new range.
- if (child.isContainerNode())
- disconnectSubframesIfNeeded(toContainerNode(child), RootAndDescendants);
+ if (is<ContainerNode>(child))
+ disconnectSubframesIfNeeded(downcast<ContainerNode>(child), RootAndDescendants);
}
static void willRemoveChildren(ContainerNode& container)
@@ -502,17 +501,14 @@ static void willRemoveChildren(ContainerNode& container)
getChildNodes(container, children);
ChildListMutationScope mutation(container);
- for (auto it = children.begin(); it != children.end(); ++it) {
- Node& child = it->get();
- mutation.willRemoveChild(child);
- child.notifyMutationObserversNodeWillDetach();
+ for (auto& child : children) {
+ mutation.willRemoveChild(child.get());
+ child->notifyMutationObserversNodeWillDetach();
// fire removed from document mutation events.
- dispatchChildRemovalEvents(child);
+ dispatchChildRemovalEvents(child.get());
}
- container.document().nodeChildrenWillBeRemoved(container);
-
disconnectSubframesIfNeeded(container, DescendantsOnly);
}
@@ -521,70 +517,48 @@ void ContainerNode::disconnectDescendantFrames()
disconnectSubframesIfNeeded(*this, RootAndDescendants);
}
-bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec)
+ExceptionOr<void> ContainerNode::removeChild(Node& oldChild)
{
// Check that this node is not "floating".
// If it is, it can be deleted as a side effect of sending mutation events.
ASSERT(refCount() || parentOrShadowHostNode());
- Ref<ContainerNode> protect(*this);
-
- ec = 0;
-
- // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
- if (isReadOnlyNode()) {
- ec = NO_MODIFICATION_ALLOWED_ERR;
- return false;
- }
+ Ref<ContainerNode> protectedThis(*this);
// NOT_FOUND_ERR: Raised if oldChild is not a child of this node.
- if (!oldChild || oldChild->parentNode() != this) {
- ec = NOT_FOUND_ERR;
- return false;
- }
-
- Ref<Node> child(*oldChild);
-
- document().removeFocusedNodeOfSubtree(&child.get());
+ if (oldChild.parentNode() != this)
+ return Exception { NOT_FOUND_ERR };
-#if ENABLE(FULLSCREEN_API)
- document().removeFullScreenElementOfSubtree(&child.get());
-#endif
-
- // Events fired when blurring currently focused node might have moved this
- // child into a different parent.
- if (child->parentNode() != this) {
- ec = NOT_FOUND_ERR;
- return false;
- }
+ Ref<Node> child(oldChild);
- willRemoveChild(child.get());
+ willRemoveChild(*this, child);
- // Mutation events might have moved this child into a different parent.
- if (child->parentNode() != this) {
- ec = NOT_FOUND_ERR;
- return false;
- }
+ // Mutation events in willRemoveChild might have moved this child into a different parent.
+ if (child->parentNode() != this)
+ return Exception { NOT_FOUND_ERR };
{
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ document().nodeWillBeRemoved(child);
Node* prev = child->previousSibling();
Node* next = child->nextSibling();
- removeBetween(prev, next, child.get());
+ removeBetween(prev, next, child);
- notifyChildRemoved(child.get(), prev, next, ChildChangeSourceAPI);
-
- ChildNodeRemovalNotifier(*this).notify(child.get());
+ notifyChildRemoved(child, prev, next, ChildChangeSourceAPI);
}
+
+ rebuildSVGExtensionsElementsIfNecessary();
dispatchSubtreeModifiedEvent();
- return true;
+ return { };
}
void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
{
- InspectorInstrumentation::didRemoveDOMNode(&oldChild.document(), &oldChild);
+ InspectorInstrumentation::didRemoveDOMNode(oldChild.document(), oldChild);
NoEventDispatchAssertion assertNoEventDispatch;
@@ -592,40 +566,116 @@ void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& ol
destroyRenderTreeIfNeeded(oldChild);
- if (nextChild)
+ if (nextChild) {
nextChild->setPreviousSibling(previousChild);
- if (previousChild)
+ oldChild.setNextSibling(nullptr);
+ } else {
+ ASSERT(m_lastChild == &oldChild);
+ m_lastChild = previousChild;
+ }
+ if (previousChild) {
previousChild->setNextSibling(nextChild);
- if (m_firstChild == &oldChild)
+ oldChild.setPreviousSibling(nullptr);
+ } else {
+ ASSERT(m_firstChild == &oldChild);
m_firstChild = nextChild;
- if (m_lastChild == &oldChild)
- m_lastChild = previousChild;
+ }
- oldChild.setPreviousSibling(0);
- oldChild.setNextSibling(0);
- oldChild.setParentNode(0);
+ ASSERT(m_firstChild != &oldChild);
+ ASSERT(m_lastChild != &oldChild);
+ ASSERT(!oldChild.previousSibling());
+ ASSERT(!oldChild.nextSibling());
+ oldChild.setParentNode(nullptr);
- document().adoptIfNeeded(&oldChild);
+ document().adoptIfNeeded(oldChild);
}
void ContainerNode::parserRemoveChild(Node& oldChild)
{
- ASSERT(oldChild.parentNode() == this);
- ASSERT(!oldChild.isDocumentFragment());
+ disconnectSubframesIfNeeded(*this, DescendantsOnly);
+ if (oldChild.parentNode() != this)
+ return;
- Node* prev = oldChild.previousSibling();
- Node* next = oldChild.nextSibling();
+ {
+ NoEventDispatchAssertion assertNoEventDispatch;
- oldChild.updateAncestorConnectedSubframeCountForRemoval();
+ document().nodeChildrenWillBeRemoved(*this);
- ChildListMutationScope(*this).willRemoveChild(oldChild);
- oldChild.notifyMutationObserversNodeWillDetach();
+ ASSERT(oldChild.parentNode() == this);
+ ASSERT(!oldChild.isDocumentFragment());
- removeBetween(prev, next, oldChild);
+ Node* prev = oldChild.previousSibling();
+ Node* next = oldChild.nextSibling();
- notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser);
+ ChildListMutationScope(*this).willRemoveChild(oldChild);
+ oldChild.notifyMutationObserversNodeWillDetach();
- ChildNodeRemovalNotifier(*this).notify(oldChild);
+ removeBetween(prev, next, oldChild);
+
+ notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser);
+ }
+}
+
+// https://dom.spec.whatwg.org/#concept-node-replace-all
+void ContainerNode::replaceAllChildren(std::nullptr_t)
+{
+ ChildListMutationScope mutation(*this);
+ removeChildren();
+}
+
+// https://dom.spec.whatwg.org/#concept-node-replace-all
+void ContainerNode::replaceAllChildren(Ref<Node>&& node)
+{
+ // This function assumes the input node is not a DocumentFragment and is parentless to decrease complexity.
+ ASSERT(!is<DocumentFragment>(node));
+ ASSERT(!node->parentNode());
+
+ if (!hasChildNodes()) {
+ // appendChildWithoutPreInsertionValidityCheck() can only throw when node has a parent and we already asserted it doesn't.
+ auto result = appendChildWithoutPreInsertionValidityCheck(node);
+ ASSERT_UNUSED(result, !result.hasException());
+ return;
+ }
+
+ Ref<ContainerNode> protectedThis(*this);
+ ChildListMutationScope mutation(*this);
+
+ // If node is not null, adopt node into parent's node document.
+ treeScope().adoptIfNeeded(node);
+
+ // Remove all parent's children, in tree order.
+ willRemoveChildren(*this);
+
+ {
+ WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
+ {
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ document().nodeChildrenWillBeRemoved(*this);
+
+ while (RefPtr<Node> child = m_firstChild) {
+ removeBetween(nullptr, child->nextSibling(), *child);
+ notifyChildNodeRemoved(*this, *child);
+ }
+
+ // If node is not null, insert node into parent before null.
+ ASSERT(!ensurePreInsertionValidity(node, nullptr).hasException());
+ InspectorInstrumentation::willInsertDOMNode(document(), *this);
+
+ appendChildCommon(node);
+ }
+
+ updateTreeAfterInsertion(node, ReplacedAllChildren::Yes);
+ }
+
+ rebuildSVGExtensionsElementsIfNecessary();
+ dispatchSubtreeModifiedEvent();
+}
+
+inline void ContainerNode::rebuildSVGExtensionsElementsIfNecessary()
+{
+ if (document().svgExtensions() && !is<SVGUseElement>(shadowHost()))
+ document().accessSVGExtensions().rebuildElements();
}
// this differs from other remove functions because it forcibly removes all the children,
@@ -636,187 +686,106 @@ void ContainerNode::removeChildren()
return;
// The container node can be removed from event handlers.
- Ref<ContainerNode> protect(*this);
-
- // exclude this node when looking for removed focusedNode since only children will be removed
- document().removeFocusedNodeOfSubtree(this, true);
-
-#if ENABLE(FULLSCREEN_API)
- document().removeFullScreenElementOfSubtree(this, true);
-#endif
+ Ref<ContainerNode> protectedThis(*this);
// Do any prep work needed before actually starting to detach
// and remove... e.g. stop loading frames, fire unload events.
willRemoveChildren(*this);
- NodeVector removedChildren;
{
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
- {
- NoEventDispatchAssertion assertNoEventDispatch;
- removedChildren.reserveInitialCapacity(childNodeCount());
- while (RefPtr<Node> n = m_firstChild) {
- removedChildren.append(*m_firstChild);
- removeBetween(0, m_firstChild->nextSibling(), *m_firstChild);
- }
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ document().nodeChildrenWillBeRemoved(*this);
+
+ while (RefPtr<Node> child = m_firstChild) {
+ removeBetween(0, child->nextSibling(), *child);
+ notifyChildNodeRemoved(*this, *child);
}
ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceAPI };
childrenChanged(change);
-
- for (size_t i = 0; i < removedChildren.size(); ++i)
- ChildNodeRemovalNotifier(*this).notify(removedChildren[i].get());
}
+ rebuildSVGExtensionsElementsIfNecessary();
dispatchSubtreeModifiedEvent();
}
-bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
+ExceptionOr<void> ContainerNode::appendChild(Node& newChild)
{
- Ref<ContainerNode> protect(*this);
-
// Check that this node is not "floating".
// If it is, it can be deleted as a side effect of sending mutation events.
ASSERT(refCount() || parentOrShadowHostNode());
- ec = 0;
-
// Make sure adding the new child is ok
- if (!checkAddChild(this, newChild.get(), ec))
- return false;
+ auto validityCheckResult = ensurePreInsertionValidity(newChild, nullptr);
+ if (validityCheckResult.hasException())
+ return validityCheckResult.releaseException();
+
+ return appendChildWithoutPreInsertionValidityCheck(newChild);
+}
- if (newChild == m_lastChild) // nothing to do
- return newChild;
+ExceptionOr<void> ContainerNode::appendChildWithoutPreInsertionValidityCheck(Node& newChild)
+{
+ Ref<ContainerNode> protectedThis(*this);
NodeVector targets;
- collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec);
- if (ec)
- return false;
+ auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
+ if (removeResult.hasException())
+ return removeResult.releaseException();
if (targets.isEmpty())
- return true;
+ return { };
// We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
- if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec))
- return false;
+ auto nodeTypeResult = checkAcceptChildGuaranteedNodeTypes(*this, newChild);
+ if (nodeTypeResult.hasException())
+ return nodeTypeResult.releaseException();
- InspectorInstrumentation::willInsertDOMNode(&document(), this);
+ InspectorInstrumentation::willInsertDOMNode(document(), *this);
// Now actually add the child(ren)
ChildListMutationScope mutation(*this);
- for (auto it = targets.begin(), end = targets.end(); it != end; ++it) {
- Node& child = it->get();
-
+ for (auto& child : targets) {
// If the child has a parent again, just stop what we're doing, because
// that means someone is doing something with DOM mutation -- can't re-parent
// a child that already has a parent.
- if (child.parentNode())
+ if (child->parentNode())
break;
- treeScope().adoptIfNeeded(&child);
-
// Append child to the end of the list
{
NoEventDispatchAssertion assertNoEventDispatch;
- appendChildToContainer(&child, *this);
+ treeScope().adoptIfNeeded(child);
+ appendChildCommon(child);
}
- updateTreeAfterInsertion(child);
+ updateTreeAfterInsertion(child.get());
}
dispatchSubtreeModifiedEvent();
- return true;
+ return { };
}
-void ContainerNode::parserAppendChild(PassRefPtr<Node> newChild)
+void ContainerNode::parserAppendChild(Node& newChild)
{
- ASSERT(newChild);
- ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
- ASSERT(!newChild->isDocumentFragment());
-#if ENABLE(TEMPLATE_ELEMENT)
+ ASSERT(!newChild.parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
+ ASSERT(!newChild.isDocumentFragment());
ASSERT(!hasTagName(HTMLNames::templateTag));
-#endif
-
- if (&document() != &newChild->document())
- document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
{
NoEventDispatchAssertion assertNoEventDispatch;
- // FIXME: This method should take a PassRefPtr.
- appendChildToContainer(newChild.get(), *this);
- treeScope().adoptIfNeeded(newChild.get());
- }
-
- newChild->updateAncestorConnectedSubframeCountForInsertion();
-
- ChildListMutationScope(*this).childAdded(*newChild);
-
- notifyChildInserted(*newChild, ChildChangeSourceParser);
-
- ChildNodeInsertionNotifier(*this).notify(*newChild);
- newChild->setNeedsStyleRecalc(ReconstructRenderTree);
-}
-
-void ContainerNode::suspendPostAttachCallbacks(Document& document)
-{
- if (!s_attachDepth) {
- ASSERT(!s_shouldReEnableMemoryCacheCallsAfterAttach);
- if (Page* page = document.page()) {
- // FIXME: How can this call be specific to one Page, while the
- // s_attachDepth is a global? Doesn't make sense.
- if (page->areMemoryCacheClientCallsEnabled()) {
- page->setMemoryCacheClientCallsEnabled(false);
- s_shouldReEnableMemoryCacheCallsAfterAttach = true;
- }
- }
- platformStrategies()->loaderStrategy()->resourceLoadScheduler()->suspendPendingRequests();
- }
- ++s_attachDepth;
-}
+ if (&document() != &newChild.document())
+ document().adoptNode(newChild);
-void ContainerNode::resumePostAttachCallbacks(Document& document)
-{
- if (s_attachDepth == 1) {
- Ref<Document> protect(document);
-
- if (s_postAttachCallbackQueue)
- dispatchPostAttachCallbacks();
- if (s_shouldReEnableMemoryCacheCallsAfterAttach) {
- s_shouldReEnableMemoryCacheCallsAfterAttach = false;
- if (Page* page = document.page())
- page->setMemoryCacheClientCallsEnabled(true);
- }
- platformStrategies()->loaderStrategy()->resourceLoadScheduler()->resumePendingRequests();
+ appendChildCommon(newChild);
+ treeScope().adoptIfNeeded(newChild);
}
- --s_attachDepth;
-}
-void ContainerNode::queuePostAttachCallback(NodeCallback callback, Node& node, unsigned callbackData)
-{
- if (!s_postAttachCallbackQueue)
- s_postAttachCallbackQueue = new NodeCallbackQueue;
-
- s_postAttachCallbackQueue->append(CallbackInfo(callback, CallbackParameters(&node, callbackData)));
-}
+ newChild.updateAncestorConnectedSubframeCountForInsertion();
-bool ContainerNode::postAttachCallbacksAreSuspended()
-{
- return s_attachDepth;
-}
-
-void ContainerNode::dispatchPostAttachCallbacks()
-{
- // We recalculate size() each time through the loop because a callback
- // can add more callbacks to the end of the queue.
- for (size_t i = 0; i < s_postAttachCallbackQueue->size(); ++i) {
- const CallbackInfo& info = (*s_postAttachCallbackQueue)[i];
- NodeCallback callback = info.first;
- CallbackParameters params = info.second;
-
- callback(*params.first, params.second);
- }
- s_postAttachCallbackQueue->clear();
+ notifyChildInserted(newChild, changeForChildInsertion(newChild, ChildChangeSourceParser));
}
void ContainerNode::childrenChanged(const ChildChange& change)
@@ -827,182 +796,30 @@ void ContainerNode::childrenChanged(const ChildChange& change)
invalidateNodeListAndCollectionCachesInAncestors();
}
-inline static void cloneChildNodesAvoidingDeleteButton(ContainerNode* parent, ContainerNode* clonedParent, HTMLElement* deleteButtonContainerElement)
-{
- ExceptionCode ec = 0;
- for (Node* child = parent->firstChild(); child && !ec; child = child->nextSibling()) {
-
-#if ENABLE(DELETION_UI)
- if (child == deleteButtonContainerElement)
- continue;
-#else
- UNUSED_PARAM(deleteButtonContainerElement);
-#endif
-
- RefPtr<Node> clonedChild = child->cloneNode(false);
- clonedParent->appendChild(clonedChild, ec);
-
- if (!ec && child->isContainerNode())
- cloneChildNodesAvoidingDeleteButton(toContainerNode(child), toContainerNode(clonedChild.get()), deleteButtonContainerElement);
- }
-}
-
-void ContainerNode::cloneChildNodes(ContainerNode *clone)
-{
-#if ENABLE(DELETION_UI)
- HTMLElement* deleteButtonContainerElement = 0;
- if (Frame* frame = document().frame())
- deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement();
- cloneChildNodesAvoidingDeleteButton(this, clone, deleteButtonContainerElement);
-#else
- cloneChildNodesAvoidingDeleteButton(this, clone, 0);
-#endif
-}
-
-bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const
-{
- if (!renderer())
- return false;
- // What is this code really trying to do?
- RenderObject* o = renderer();
- RenderObject* p = o;
-
- if (!o->isInline() || o->isReplaced()) {
- point = o->localToAbsolute(FloatPoint(), UseTransforms);
- return true;
- }
-
- // find the next text/image child, to get a position
- while (o) {
- p = o;
- if (RenderObject* child = o->firstChildSlow())
- o = child;
- else if (o->nextSibling())
- o = o->nextSibling();
- else {
- RenderObject* next = 0;
- while (!next && o->parent()) {
- o = o->parent();
- next = o->nextSibling();
- }
- o = next;
-
- if (!o)
- break;
- }
- ASSERT(o);
-
- if (!o->isInline() || o->isReplaced()) {
- point = o->localToAbsolute(FloatPoint(), UseTransforms);
- return true;
- }
-
- if (p->node() && p->node() == this && o->isText() && !toRenderText(o)->firstTextBox()) {
- // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor
- } else if (o->isText() || o->isReplaced()) {
- point = FloatPoint();
- if (o->isText() && toRenderText(o)->firstTextBox()) {
- point.move(toRenderText(o)->linesBoundingBox().x(), toRenderText(o)->firstTextBox()->root().lineTop());
- } else if (o->isBox()) {
- RenderBox* box = toRenderBox(o);
- point.moveBy(box->location());
- }
- point = o->container()->localToAbsolute(point, UseTransforms);
- return true;
- }
- }
-
- // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
- // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
- if (!o && document().view()) {
- point = FloatPoint(0, document().view()->contentsHeight());
- return true;
- }
- return false;
-}
-
-bool ContainerNode::getLowerRightCorner(FloatPoint& point) const
+void ContainerNode::cloneChildNodes(ContainerNode& clone)
{
- if (!renderer())
- return false;
-
- RenderObject* o = renderer();
- if (!o->isInline() || o->isReplaced()) {
- RenderBox* box = toRenderBox(o);
- point = o->localToAbsolute(LayoutPoint(box->size()), UseTransforms);
- return true;
+ Document& targetDocument = clone.document();
+ for (Node* child = firstChild(); child; child = child->nextSibling()) {
+ auto clonedChild = child->cloneNodeInternal(targetDocument, CloningOperation::SelfWithTemplateContent);
+ if (!clone.appendChild(clonedChild).hasException() && is<ContainerNode>(*child))
+ downcast<ContainerNode>(*child).cloneChildNodes(downcast<ContainerNode>(clonedChild.get()));
}
-
- // find the last text/image child, to get a position
- while (o) {
- if (RenderObject* child = o->lastChildSlow())
- o = child;
- else if (o->previousSibling())
- o = o->previousSibling();
- else {
- RenderObject* prev = 0;
- while (!prev) {
- o = o->parent();
- if (!o)
- return false;
- prev = o->previousSibling();
- }
- o = prev;
- }
- ASSERT(o);
- if (o->isText() || o->isReplaced()) {
- point = FloatPoint();
- if (o->isText()) {
- RenderText* text = toRenderText(o);
- IntRect linesBox = text->linesBoundingBox();
- if (!linesBox.maxX() && !linesBox.maxY())
- continue;
- point.moveBy(linesBox.maxXMaxYCorner());
- } else {
- RenderBox* box = toRenderBox(o);
- point.moveBy(box->frameRect().maxXMaxYCorner());
- }
- point = o->container()->localToAbsolute(point, UseTransforms);
- return true;
- }
- }
- return true;
-}
-
-LayoutRect ContainerNode::boundingBox() const
-{
- FloatPoint upperLeft, lowerRight;
- bool foundUpperLeft = getUpperLeftCorner(upperLeft);
- bool foundLowerRight = getLowerRightCorner(lowerRight);
-
- // If we've found one corner, but not the other,
- // then we should just return a point at the corner that we did find.
- if (foundUpperLeft != foundLowerRight) {
- if (foundUpperLeft)
- lowerRight = upperLeft;
- else
- upperLeft = lowerRight;
- }
-
- return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft));
}
-unsigned ContainerNode::childNodeCount() const
+unsigned ContainerNode::countChildNodes() const
{
unsigned count = 0;
- Node *n;
- for (n = firstChild(); n; n = n->nextSibling())
- count++;
+ for (Node* child = firstChild(); child; child = child->nextSibling())
+ ++count;
return count;
}
-Node *ContainerNode::childNode(unsigned index) const
+Node* ContainerNode::traverseToChildAt(unsigned index) const
{
- unsigned i;
- Node *n = firstChild();
- for (i = 0; n != 0 && i < index; i++)
- n = n->nextSibling();
- return n;
+ Node* child = firstChild();
+ for (; child && index > 0; --index)
+ child = child->nextSibling();
+ return child;
}
static void dispatchChildInsertionEvents(Node& child)
@@ -1010,7 +827,7 @@ static void dispatchChildInsertionEvents(Node& child)
if (child.isInShadowTree())
return;
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(child));
RefPtr<Node> c = &child;
Ref<Document> document(child.document());
@@ -1019,8 +836,8 @@ static void dispatchChildInsertionEvents(Node& child)
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode()));
// dispatch the DOMNodeInsertedIntoDocument event to all descendants
- if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
- for (; c; c = NodeTraversal::next(c.get(), &child))
+ if (c->isConnected() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
+ for (; c; c = NodeTraversal::next(*c, &child))
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false));
}
}
@@ -1028,14 +845,14 @@ static void dispatchChildInsertionEvents(Node& child)
static void dispatchChildRemovalEvents(Node& child)
{
if (child.isInShadowTree()) {
- InspectorInstrumentation::willRemoveDOMNode(&child.document(), &child);
+ InspectorInstrumentation::willRemoveDOMNode(child.document(), child);
return;
}
- ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(child));
willCreatePossiblyOrphanedTreeByRemoval(&child);
- InspectorInstrumentation::willRemoveDOMNode(&child.document(), &child);
+ InspectorInstrumentation::willRemoveDOMNode(child.document(), child);
RefPtr<Node> c = &child;
Ref<Document> document(child.document());
@@ -1045,93 +862,121 @@ static void dispatchChildRemovalEvents(Node& child)
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode()));
// dispatch the DOMNodeRemovedFromDocument event to all descendants
- if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
- for (; c; c = NodeTraversal::next(c.get(), &child))
+ if (c->isConnected() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
+ for (; c; c = NodeTraversal::next(*c, &child))
c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false));
}
}
-void ContainerNode::updateTreeAfterInsertion(Node& child)
+void ContainerNode::updateTreeAfterInsertion(Node& child, ReplacedAllChildren replacedAllChildren)
{
ASSERT(child.refCount());
- ChildListMutationScope(*this).childAdded(child);
-
- notifyChildInserted(child, ChildChangeSourceAPI);
-
- ChildNodeInsertionNotifier(*this).notify(child);
-
- child.setNeedsStyleRecalc(ReconstructRenderTree);
+ notifyChildInserted(child, changeForChildInsertion(child, ChildChangeSourceAPI, replacedAllChildren));
dispatchChildInsertionEvents(child);
}
-void ContainerNode::setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue)
+ExceptionOr<Element*> ContainerNode::querySelector(const String& selectors)
{
- setAttributeEventListener(eventType, JSLazyEventListener::createForNode(*this, attributeName, attributeValue));
+ auto query = document().selectorQueryForString(selectors);
+ if (query.hasException())
+ return query.releaseException();
+ return query.releaseReturnValue().queryFirst(*this);
}
-Element* ContainerNode::querySelector(const AtomicString& selectors, ExceptionCode& ec)
+ExceptionOr<Ref<NodeList>> ContainerNode::querySelectorAll(const String& selectors)
{
- if (selectors.isEmpty()) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
+ auto query = document().selectorQueryForString(selectors);
+ if (query.hasException())
+ return query.releaseException();
+ return query.releaseReturnValue().queryAll(*this);
+}
- SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), ec);
- if (!selectorQuery)
- return nullptr;
- return selectorQuery->queryFirst(*this);
+Ref<HTMLCollection> ContainerNode::getElementsByTagName(const AtomicString& qualifiedName)
+{
+ ASSERT(!qualifiedName.isNull());
+
+ if (qualifiedName == starAtom)
+ return ensureRareData().ensureNodeLists().addCachedCollection<AllDescendantsCollection>(*this, AllDescendants);
+
+ if (document().isHTMLDocument())
+ return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTagCollection>(*this, ByHTMLTag, qualifiedName);
+ return ensureRareData().ensureNodeLists().addCachedCollection<TagCollection>(*this, ByTag, qualifiedName);
}
-RefPtr<NodeList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionCode& ec)
+Ref<HTMLCollection> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
{
- if (selectors.isEmpty()) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
+ ASSERT(!localName.isNull());
+ return ensureRareData().ensureNodeLists().addCachedTagCollectionNS(*this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
+}
+
+Ref<NodeList> ContainerNode::getElementsByName(const String& elementName)
+{
+ return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, elementName);
+}
- SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), ec);
- if (!selectorQuery)
- return nullptr;
- return selectorQuery->queryAll(*this);
+Ref<HTMLCollection> ContainerNode::getElementsByClassName(const AtomicString& classNames)
+{
+ return ensureRareData().ensureNodeLists().addCachedCollection<ClassCollection>(*this, ByClass, classNames);
}
-PassRefPtr<NodeList> ContainerNode::getElementsByTagName(const AtomicString& localName)
+Ref<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name)
{
- if (localName.isNull())
- return 0;
+ ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag));
+ return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, name);
+}
- if (document().isHTMLDocument())
- return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLTagNodeList>(*this, LiveNodeList::HTMLTagNodeListType, localName);
- return ensureRareData().ensureNodeLists().addCacheWithAtomicName<TagNodeList>(*this, LiveNodeList::TagNodeListType, localName);
+Ref<HTMLCollection> ContainerNode::children()
+{
+ return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<NodeChildren>::traversalType>>(*this, NodeChildren);
}
-PassRefPtr<NodeList> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
+Element* ContainerNode::firstElementChild() const
{
- if (localName.isNull())
- return 0;
+ return ElementTraversal::firstChild(*this);
+}
- if (namespaceURI == starAtom)
- return getElementsByTagName(localName);
+Element* ContainerNode::lastElementChild() const
+{
+ return ElementTraversal::lastChild(*this);
+}
- return ensureRareData().ensureNodeLists().addCacheWithQualifiedName(*this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
+unsigned ContainerNode::childElementCount() const
+{
+ auto children = childrenOfType<Element>(*this);
+ return std::distance(children.begin(), children.end());
}
-PassRefPtr<NodeList> ContainerNode::getElementsByName(const String& elementName)
+ExceptionOr<void> ContainerNode::append(Vector<NodeOrString>&& vector)
{
- return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, LiveNodeList::NameNodeListType, elementName);
+ auto result = convertNodesOrStringsIntoNode(WTFMove(vector));
+ if (result.hasException())
+ return result.releaseException();
+
+ auto node = result.releaseReturnValue();
+ if (!node)
+ return { };
+
+ return appendChild(*node);
}
-PassRefPtr<NodeList> ContainerNode::getElementsByClassName(const String& classNames)
+ExceptionOr<void> ContainerNode::prepend(Vector<NodeOrString>&& vector)
{
- return ensureRareData().ensureNodeLists().addCacheWithName<ClassNodeList>(*this, LiveNodeList::ClassNodeListType, classNames);
+ auto result = convertNodesOrStringsIntoNode(WTFMove(vector));
+ if (result.hasException())
+ return result.releaseException();
+
+ auto node = result.releaseReturnValue();
+ if (!node)
+ return { };
+
+ return insertBefore(*node, firstChild());
}
-PassRefPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name)
+HTMLCollection* ContainerNode::cachedHTMLCollection(CollectionType type)
{
- ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag));
- return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, LiveNodeList::RadioNodeListType, name);
+ return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : nullptr;
}
} // namespace WebCore