#include "config.h"
#include "ShadowTree.h"
#include "ContainerNodeAlgorithms.h"
#include "Document.h"
#include "Element.h"
#include "HTMLShadowElement.h"
#include "InspectorInstrumentation.h"
#include "RuntimeEnabledFeatures.h"
#include "ShadowRoot.h"
#include "StyleResolver.h"
#include "Text.h"
namespace WebCore {
ShadowTree::ShadowTree()
: m_needsRecalculateContent(false)
{
}
ShadowTree::~ShadowTree()
{
if (hasShadowRoot())
removeAllShadowRoots();
}
static bool validateShadowRoot(Document* document, ShadowRoot* shadowRoot, ExceptionCode& ec)
{
if (!shadowRoot)
return true;
if (shadowRoot->shadowHost()) {
ec = HIERARCHY_REQUEST_ERR;
return false;
}
if (shadowRoot->document() != document) {
ec = WRONG_DOCUMENT_ERR;
return false;
}
return true;
}
void ShadowTree::addShadowRoot(Element* shadowHost, PassRefPtr<ShadowRoot> shadowRoot, ExceptionCode& ec)
{
ASSERT(shadowHost);
ASSERT(shadowRoot);
if (!validateShadowRoot(shadowHost->document(), shadowRoot.get(), ec))
return;
shadowRoot->setShadowHost(shadowHost);
ChildNodeInsertionNotifier(shadowHost).notify(shadowRoot.get());
if (shadowHost->attached()) {
shadowRoot->lazyAttach();
detach();
shadowHost->detachChildren();
}
m_shadowRoots.push(shadowRoot.get());
InspectorInstrumentation::didPushShadowRoot(shadowHost, shadowRoot.get());
}
void ShadowTree::removeAllShadowRoots()
{
if (!hasShadowRoot())
return;
Element* shadowHost = host();
while (RefPtr<ShadowRoot> oldRoot = m_shadowRoots.removeHead()) {
InspectorInstrumentation::willPopShadowRoot(shadowHost, oldRoot.get());
shadowHost->document()->removeFocusedNodeOfSubtree(oldRoot.get());
if (oldRoot->attached())
oldRoot->detach();
oldRoot->setShadowHost(0);
oldRoot->setPrev(0);
oldRoot->setNext(0);
shadowHost->document()->adoptIfNeeded(oldRoot.get());
ChildNodeRemovalNotifier(shadowHost).notify(oldRoot.get());
}
if (shadowHost->attached())
shadowHost->attachChildrenLazily();
}
void ShadowTree::setParentTreeScope(TreeScope* scope)
{
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
root->setParentTreeScope(scope);
}
void ShadowTree::attach()
{
ASSERT(!selector().hasPopulated());
selector().willSelect();
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
if (!root->attached())
root->attach();
}
selector().didSelect();
}
void ShadowTree::attachHost(Element* host)
{
attach();
host->attachChildrenIfNeeded();
host->attachAsNode();
}
void ShadowTree::detach()
{
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
if (root->attached())
root->detach();
}
}
void ShadowTree::detachHost(Element* host)
{
host->detachChildrenIfNeeded();
detach();
host->detachAsNode();
}
InsertionPoint* ShadowTree::insertionPointFor(const Node* node) const
{
ASSERT(node && node->parentNode());
if (node->parentNode()->isShadowRoot()) {
if (InsertionPoint* insertionPoint = toShadowRoot(node->parentNode())->assignedTo())
return insertionPoint;
return 0;
}
HTMLContentSelection* found = selector().findFor(node);
if (!found)
return 0;
return found->insertionPoint();
}
HTMLContentSelection* ShadowTree::selectionFor(const Node* node) const
{
return m_selector.findFor(node);
}
void ShadowTree::reattach()
{
detach();
attach();
}
bool ShadowTree::childNeedsStyleRecalc()
{
ASSERT(youngestShadowRoot());
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
if (root->childNeedsStyleRecalc())
return true;
return false;
}
bool ShadowTree::needsStyleRecalc()
{
ASSERT(youngestShadowRoot());
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
if (root->needsStyleRecalc())
return true;
return false;
}
void ShadowTree::recalcShadowTreeStyle(Node::StyleChange change)
{
ShadowRoot* youngest = youngestShadowRoot();
if (!youngest)
return;
if (needsReattachHostChildrenAndShadow())
reattachHostChildrenAndShadow();
else {
StyleResolver* styleResolver = youngest->document()->styleResolver();
styleResolver->pushParentShadowRoot(youngest);
for (Node* n = youngest->firstChild(); n; n = n->nextSibling()) {
if (n->isElementNode())
static_cast<Element*>(n)->recalcStyle(change);
else if (n->isTextNode())
toText(n)->recalcTextStyle(change);
}
styleResolver->popParentShadowRoot(youngest);
}
clearNeedsReattachHostChildrenAndShadow();
for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot()) {
root->clearNeedsStyleRecalc();
root->clearChildNeedsStyleRecalc();
}
}
bool ShadowTree::needsReattachHostChildrenAndShadow()
{
return m_needsRecalculateContent || (youngestShadowRoot() && youngestShadowRoot()->hasInsertionPoint());
}
void ShadowTree::hostChildrenChanged()
{
ASSERT(youngestShadowRoot());
if (!youngestShadowRoot()->hasInsertionPoint())
return;
setNeedsReattachHostChildrenAndShadow();
}
void ShadowTree::setNeedsReattachHostChildrenAndShadow()
{
m_needsRecalculateContent = true;
host()->setNeedsStyleRecalc();
}
void ShadowTree::reattachHostChildrenAndShadow()
{
ASSERT(youngestShadowRoot());
Element* hostNode = youngestShadowRoot()->host();
hostNode->detachChildrenIfNeeded();
reattach();
hostNode->attachChildrenIfNeeded();
}
}