ContainerNodeAlgorithms.cpp [plain text]
#include "config.h"
#include "ContainerNodeAlgorithms.h"
#include "HTMLFrameOwnerElement.h"
#include "HTMLTextAreaElement.h"
#include "InspectorInstrumentation.h"
#include "NoEventDispatchAssertion.h"
#include "ShadowRoot.h"
#include "TypedElementDescendantIterator.h"
namespace WebCore {
static void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
{
ASSERT(insertionPoint.isConnected());
if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
postInsertionNotificationTargets.append(node);
if (!is<ContainerNode>(node))
return;
ChildNodesLazySnapshot snapshot(downcast<ContainerNode>(node));
while (RefPtr<Node> child = snapshot.nextNode()) {
if (node.isConnected() && child->parentNode() == &node)
notifyNodeInsertedIntoDocument(insertionPoint, *child, postInsertionNotificationTargets);
}
if (!is<Element>(node))
return;
if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
if (node.isConnected() && root->host() == &node)
notifyNodeInsertedIntoDocument(insertionPoint, *root, postInsertionNotificationTargets);
}
}
static void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
{
NoEventDispatchAssertion assertNoEventDispatch;
ASSERT(!insertionPoint.isConnected());
if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
postInsertionNotificationTargets.append(node);
if (!is<ContainerNode>(node))
return;
for (auto* child = downcast<ContainerNode>(node).firstChild(); child; child = child->nextSibling())
notifyNodeInsertedIntoTree(insertionPoint, *child, postInsertionNotificationTargets);
if (!is<Element>(node))
return;
if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets);
}
void notifyChildNodeInserted(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
{
ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(insertionPoint));
InspectorInstrumentation::didInsertDOMNode(node.document(), node);
Ref<Document> protectDocument(node.document());
Ref<Node> protectNode(node);
if (insertionPoint.isConnected())
notifyNodeInsertedIntoDocument(insertionPoint, node, postInsertionNotificationTargets);
else
notifyNodeInsertedIntoTree(insertionPoint, node, postInsertionNotificationTargets);
}
static void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node& node)
{
ASSERT(insertionPoint.isConnected());
node.removedFrom(insertionPoint);
if (!is<ContainerNode>(node))
return;
ChildNodesLazySnapshot snapshot(downcast<ContainerNode>(node));
while (RefPtr<Node> child = snapshot.nextNode()) {
if (!node.isConnected() && child->parentNode() == &node)
notifyNodeRemovedFromDocument(insertionPoint, *child.get());
}
if (!is<Element>(node))
return;
if (node.document().cssTarget() == &node)
node.document().setCSSTarget(nullptr);
if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
if (!node.isConnected() && root->host() == &node)
notifyNodeRemovedFromDocument(insertionPoint, *root.get());
}
}
static void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, Node& node)
{
NoEventDispatchAssertion assertNoEventDispatch;
ASSERT(!insertionPoint.isConnected());
node.removedFrom(insertionPoint);
for (Node* child = node.firstChild(); child; child = child->nextSibling())
notifyNodeRemovedFromTree(insertionPoint, *child);
if (!is<Element>(node))
return;
if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
notifyNodeRemovedFromTree(insertionPoint, *root);
}
void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child)
{
if (child.isConnected())
notifyNodeRemovedFromDocument(insertionPoint, child);
else
notifyNodeRemovedFromTree(insertionPoint, child);
}
void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
{
Node* next = nullptr;
for (auto* node = container.firstChild(); node; node = next) {
ASSERT(!node->m_deletionHasBegun);
next = node->nextSibling();
node->setNextSibling(nullptr);
node->setParentNode(nullptr);
container.setFirstChild(next);
if (next)
next->setPreviousSibling(nullptr);
if (!node->refCount()) {
#ifndef NDEBUG
node->m_deletionHasBegun = true;
#endif
if (tail)
tail->setNextSibling(node);
else
head = node;
tail = node;
} else {
Ref<Node> protect(*node); node->setTreeScopeRecursively(container.document());
if (node->isInTreeScope())
notifyChildNodeRemoved(container, *node);
ASSERT_WITH_SECURITY_IMPLICATION(!node->isInTreeScope());
}
}
container.setLastChild(nullptr);
}
void removeDetachedChildrenInContainer(ContainerNode& container)
{
Node* head = nullptr;
Node* tail = nullptr;
addChildNodesToDeletionQueue(head, tail, container);
Node* node;
Node* next;
while ((node = head)) {
ASSERT(node->m_deletionHasBegun);
next = node->nextSibling();
node->setNextSibling(nullptr);
head = next;
if (!next)
tail = nullptr;
if (is<ContainerNode>(*node))
addChildNodesToDeletionQueue(head, tail, downcast<ContainerNode>(*node));
delete node;
}
}
#ifndef NDEBUG
static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
{
unsigned count = 0;
if (is<Element>(node)) {
if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame())
++count;
if (ShadowRoot* root = downcast<Element>(node).shadowRoot())
count += assertConnectedSubrameCountIsConsistent(*root);
}
for (auto& child : childrenOfType<Element>(node))
count += assertConnectedSubrameCountIsConsistent(child);
ASSERT(node.connectedSubframeCount() >= count);
ASSERT(node.connectedSubframeCount() == count);
return count;
}
#endif
static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners, ContainerNode& root)
{
auto elementDescendants = descendantsOfType<Element>(root);
auto it = elementDescendants.begin();
auto end = elementDescendants.end();
while (it != end) {
Element& element = *it;
if (!element.connectedSubframeCount()) {
it.traverseNextSkippingChildren();
continue;
}
if (is<HTMLFrameOwnerElement>(element))
frameOwners.append(downcast<HTMLFrameOwnerElement>(element));
if (ShadowRoot* shadowRoot = element.shadowRoot())
collectFrameOwners(frameOwners, *shadowRoot);
++it;
}
}
void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy)
{
#ifndef NDEBUG
assertConnectedSubrameCountIsConsistent(root);
#endif
ASSERT(root.connectedSubframeCount());
Vector<Ref<HTMLFrameOwnerElement>> frameOwners;
if (policy == RootAndDescendants) {
if (is<HTMLFrameOwnerElement>(root))
frameOwners.append(downcast<HTMLFrameOwnerElement>(root));
}
collectFrameOwners(frameOwners, root);
if (auto* shadowRoot = root.shadowRoot())
collectFrameOwners(frameOwners, *shadowRoot);
SubframeLoadingDisabler disabler(&root);
bool isFirst = true;
for (auto& owner : frameOwners) {
if (isFirst || root.containsIncludingShadowDOM(&owner.get()))
owner.get().disconnectContentFrame();
isFirst = false;
}
}
}