static ContainerNode* traverseParent(const Node* node, ShadowRootCrossing shadowRootCrossing) { if (node->isPseudoElement()) return toPseudoElement(node)->hostElement(); if (shadowRootCrossing == DontCrossShadowRoot && node->isShadowRoot()) return 0; if (nodeCanBeDistributed(node)) { if (InsertionPoint* insertionPoint = findInsertionPointOf(node)) return traverseParent(insertionPoint, shadowRootCrossing); return nullptr; } ContainerNode* parent = node->parentNode(); if (!parent) return nullptr; if (parent->isShadowRoot()) return shadowRootCrossing == CrossShadowRoot ? toShadowRoot(parent)->hostElement() : parent; if (parent->isInsertionPoint()) { const InsertionPoint* insertionPoint = toInsertionPoint(parent); if (insertionPoint->hasDistribution()) return nullptr; if (insertionPoint->isActive()) return traverseParent(parent, shadowRootCrossing); } return parent; }
void EventDispatcher::ensureEventAncestors(Event* event) { if (m_ancestorsInitialized) return; m_ancestorsInitialized = true; bool inDocument = m_node->inDocument(); bool isSVGElement = m_node->isSVGElement(); Vector<EventTarget*> targetStack; Node* last = 0; for (ComposedShadowTreeParentWalker walker(m_node.get()); walker.get(); walker.parentIncludingInsertionPointAndShadowRoot()) { Node* node = walker.get(); if (targetStack.isEmpty()) targetStack.append(eventTargetRespectingSVGTargetRules(node)); else if (isInsertionPoint(node) && toInsertionPoint(node)->contains(last)) targetStack.append(targetStack.last()); m_ancestors.append(EventContext(node, eventTargetRespectingSVGTargetRules(node), targetStack.last())); if (!inDocument) return; last = node; if (!node->isShadowRoot()) continue; if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) return; if (!isSVGElement) { ASSERT(!targetStack.isEmpty()); targetStack.removeLast(); } } }
void ContentDistributor::distributeNodeChildrenTo(InsertionPoint* insertionPoint, ContainerNode* containerNode) { ContentDistribution distribution; for (Node* node = containerNode->firstChild(); node; node = node->nextSibling()) { if (isActiveInsertionPoint(node)) { InsertionPoint* innerInsertionPoint = toInsertionPoint(node); if (innerInsertionPoint->hasDistribution()) { for (size_t i = 0; i < innerInsertionPoint->size(); ++i) { distribution.append(innerInsertionPoint->at(i)); if (!m_nodeToInsertionPoint.contains(innerInsertionPoint->at(i))) m_nodeToInsertionPoint.add(innerInsertionPoint->at(i), insertionPoint); } } else { for (Node* child = innerInsertionPoint->firstChild(); child; child = child->nextSibling()) { distribution.append(child); m_nodeToInsertionPoint.add(child, insertionPoint); } } } else { distribution.append(node); if (!m_nodeToInsertionPoint.contains(node)) m_nodeToInsertionPoint.add(node, insertionPoint); } } insertionPoint->setDistribution(distribution); }
inline Node* ComposedShadowTreeWalker::traverseNodeEscapingFallbackContents(const Node* node, ParentTraversalDetails* details) const { ASSERT(node); if (!node->isInsertionPoint()) return const_cast<Node*>(node); const InsertionPoint* insertionPoint = toInsertionPoint(node); return insertionPoint->hasDistribution() ? 0 : insertionPoint->isActive() ? traverseParent(node, details) : const_cast<Node*>(node); }
bool Internals::isValidContentSelect(Element* insertionPoint, ExceptionCode& ec) { if (!insertionPoint || !isInsertionPoint(insertionPoint)) { ec = INVALID_ACCESS_ERR; return false; } return toInsertionPoint(insertionPoint)->isSelectValid(); }
static Node* findLastEnteringInsertionPoints(const Node* node) { ASSERT(node); if (!isActiveInsertionPoint(node)) return const_cast<Node*>(node); const InsertionPoint* insertionPoint = toInsertionPoint(node); if (Node* found = findLastFromDistributedNode(insertionPoint->lastDistributed(), insertionPoint)) return found; return findLastSiblingEnteringInsertionPoints(node->lastChild()); }
Node* ComposedShadowTreeWalker::traverseNode(const Node* node, TraversalDirection direction) { ASSERT(node); if (!isActiveInsertionPoint(node)) return const_cast<Node*>(node); const InsertionPoint* insertionPoint = toInsertionPoint(node); if (Node* found = traverseDistributedNodes(direction == TraversalDirectionForward ? insertionPoint->first() : insertionPoint->last(), insertionPoint, direction)) return found; return traverseLightChildren(node, direction); }
Node* ComposedTreeWalker::traverseNode(const Node* node, TraversalDirection direction) { ASSERT(node); if (!isActiveInsertionPoint(*node)) return const_cast<Node*>(node); const InsertionPoint* insertionPoint = toInsertionPoint(node); if (Node* found = traverseDistributedNodes(direction == TraversalDirectionForward ? insertionPoint->first() : insertionPoint->last(), insertionPoint, direction)) return found; ASSERT(node->hasTagName(HTMLNames::shadowTag) || (node->hasTagName(HTMLNames::contentTag) && !node->hasChildNodes())); return 0; }
Node* ComposedShadowTreeWalker::traverseNode(const Node* node, TraversalDirection direction) { ASSERT(node); if (!isInsertionPoint(node)) return const_cast<Node*>(node); const InsertionPoint* insertionPoint = toInsertionPoint(node); if (!insertionPoint->isActive()) return const_cast<Node*>(node); if (Node* next = (direction == TraversalDirectionForward ? insertionPoint->first() : insertionPoint->last())) return traverseNode(next, direction); return traverseLightChildren(node, direction); }
Node* FlatTreeTraversal::v0ResolveDistributionStartingAt(const Node& node, TraversalDirection direction) { DCHECK(!isHTMLSlotElement(node)); for (const Node* sibling = &node; sibling; sibling = (direction == TraversalDirectionForward ? sibling->nextSibling() : sibling->previousSibling())) { if (!isActiveInsertionPoint(*sibling)) return const_cast<Node*>(sibling); const InsertionPoint& insertionPoint = toInsertionPoint(*sibling); if (Node* found = (direction == TraversalDirectionForward ? insertionPoint.firstDistributedNode() : insertionPoint.lastDistributedNode())) return found; DCHECK(isHTMLShadowElement(insertionPoint) || (isHTMLContentElement(insertionPoint) && !insertionPoint.hasChildren())); } return nullptr; }
void ContentDistributor::distribute(Element* host) { ASSERT(needsDistribution()); ASSERT(m_nodeToInsertionPoint.isEmpty()); m_validity = Valid; ContentDistribution pool; for (Node* node = host->firstChild(); node; node = node->nextSibling()) populate(node, pool); Vector<bool> distributed(pool.size()); distributed.fill(false); Vector<HTMLShadowElement*, 8> activeShadowInsertionPoints; for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) { HTMLShadowElement* firstActiveShadowInsertionPoint = 0; for (Node* node = root; node; node = node->traverseNextNode(root)) { if (!isActiveInsertionPoint(node)) continue; InsertionPoint* point = toInsertionPoint(node); if (isHTMLShadowElement(node)) { if (!firstActiveShadowInsertionPoint) firstActiveShadowInsertionPoint = toHTMLShadowElement(node); } else { distributeSelectionsTo(point, pool, distributed); if (ElementShadow* shadow = node->parentNode()->isElementNode() ? toElement(node->parentNode())->shadow() : 0) shadow->invalidateDistribution(); } } if (firstActiveShadowInsertionPoint) activeShadowInsertionPoints.append(firstActiveShadowInsertionPoint); } for (size_t i = activeShadowInsertionPoints.size(); i > 0; --i) { HTMLShadowElement* shadowElement = activeShadowInsertionPoints[i - 1]; ShadowRoot* root = shadowElement->shadowRoot(); ASSERT(root); if (root->olderShadowRoot()) { distributeNodeChildrenTo(shadowElement, root->olderShadowRoot()); root->olderShadowRoot()->setAssignedTo(shadowElement); } else { distributeSelectionsTo(shadowElement, pool, distributed); if (ElementShadow* shadow = shadowElement->parentNode()->isElementNode() ? toElement(shadowElement->parentNode())->shadow() : 0) shadow->invalidateDistribution(); } } }
inline void DistributionPool::populateChildren(const ContainerNode* parent) { for (Node* child = parent->firstChild(); child; child = child->nextSibling()) { if (isActiveInsertionPoint(*child)) { InsertionPoint* insertionPoint = toInsertionPoint(child); for (size_t i = 0; i < insertionPoint->size(); ++i) m_nodes.append(insertionPoint->at(i)); } else { m_nodes.append(child); } } m_distributed.resize(m_nodes.size()); m_distributed.fill(false); }
const Vector<RefPtr<InsertionPoint> >& ContentDistributor::ensureInsertionPointList(ShadowRoot* shadowRoot) { if (m_insertionPointListIsValid) return m_insertionPointList; m_insertionPointListIsValid = true; ASSERT(m_insertionPointList.isEmpty()); for (Element* element = ElementTraversal::firstWithin(shadowRoot); element; element = ElementTraversal::next(element, shadowRoot)) { if (element->isInsertionPoint()) m_insertionPointList.append(toInsertionPoint(element)); } return m_insertionPointList; }
const Vector<RefPtr<InsertionPoint>>& ContentDistributor::ensureInsertionPointList(ShadowRoot* shadowRoot) { if (m_insertionPointListIsValid) return m_insertionPointList; m_insertionPointListIsValid = true; ASSERT(m_insertionPointList.isEmpty()); for (auto& element : descendantsOfType<Element>(*shadowRoot)) { if (element.isInsertionPoint()) m_insertionPointList.append(toInsertionPoint(&element)); } return m_insertionPointList; }
void ContentDistributor::populate(Node* node, ContentDistribution& pool) { if (!isActiveInsertionPoint(node)) { pool.append(node); return; } InsertionPoint* insertionPoint = toInsertionPoint(node); if (insertionPoint->hasDistribution()) { for (size_t i = 0; i < insertionPoint->size(); ++i) populate(insertionPoint->at(i), pool); } else { for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling()) pool.append(fallbackNode); } }
const Vector<InsertionPoint*>& ShadowRootContentDistributionData::ensureInsertionPointList(ShadowRoot* shadowRoot) { if (m_insertionPointListIsValid) return m_insertionPointList; m_insertionPointListIsValid = true; ASSERT(m_insertionPointList.isEmpty()); if (!shadowRoot->hasInsertionPoint()) return m_insertionPointList; for (Node* node = shadowRoot; node; node = node->traverseNextNode(shadowRoot)) { if (node->isInsertionPoint()) m_insertionPointList.append(toInsertionPoint(node)); } return m_insertionPointList; }
inline void DistributionPool::populateChildren(const ContainerNode& parent) { clear(); for (Node* child = parent.firstChild(); child; child = child->nextSibling()) { if (isHTMLSlotElement(child)) { // TODO(hayato): Support re-distribution across v0 and v1 shadow trees continue; } if (isActiveInsertionPoint(*child)) { InsertionPoint* insertionPoint = toInsertionPoint(child); for (size_t i = 0; i < insertionPoint->distributedNodesSize(); ++i) m_nodes.append(insertionPoint->distributedNodeAt(i)); } else { m_nodes.append(child); } } m_distributed.resize(m_nodes.size()); m_distributed.fill(false); }
void EventRelatedTargetAdjuster::adjust(Vector<EventContext>& ancestors) { Vector<EventTarget*> relatedTargetStack; TreeScope* lastTreeScope = 0; Node* lastNode = 0; for (ComposedShadowTreeParentWalker walker(m_relatedTarget.get()); walker.get(); walker.parentIncludingInsertionPointAndShadowRoot()) { Node* node = walker.get(); if (relatedTargetStack.isEmpty()) relatedTargetStack.append(node); else if (isInsertionPoint(node) && toInsertionPoint(node)->contains(lastNode)) relatedTargetStack.append(relatedTargetStack.last()); TreeScope* scope = node->treeScope(); // Skips adding a node to the map if treeScope does not change. Just for the performance optimization. if (scope != lastTreeScope) m_relatedTargetMap.add(scope, relatedTargetStack.last()); lastTreeScope = scope; lastNode = node; if (node->isShadowRoot()) { ASSERT(!relatedTargetStack.isEmpty()); relatedTargetStack.removeLast(); } } lastTreeScope = 0; EventTarget* adjustedRelatedTarget = 0; for (Vector<EventContext>::iterator iter = ancestors.begin(); iter < ancestors.end(); ++iter) { TreeScope* scope = iter->node()->treeScope(); if (scope == lastTreeScope) { // Re-use the previous adjustedRelatedTarget if treeScope does not change. Just for the performance optimization. iter->setRelatedTarget(adjustedRelatedTarget); } else { adjustedRelatedTarget = findRelatedTarget(scope); iter->setRelatedTarget(adjustedRelatedTarget); } lastTreeScope = scope; if (iter->target() == adjustedRelatedTarget) { // Event dispatching should be stopped here. ancestors.shrink(iter - ancestors.begin()); break; } } }
bool ContentDistributor::invalidate(Element* host) { ASSERT(needsInvalidation()); bool needsReattach = (m_validity == Undetermined) || !m_nodeToInsertionPoint.isEmpty(); for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) { root->setAssignedTo(0); for (Node* node = root; node; node = node->traverseNextNode(root)) { if (!isInsertionPoint(node)) continue; needsReattach = needsReattach || true; InsertionPoint* point = toInsertionPoint(node); point->clearDistribution(); } } m_validity = Invalidating; m_nodeToInsertionPoint.clear(); return needsReattach; }
const Vector<RefPtr<InsertionPoint> >& ShadowRoot::descendantInsertionPoints() { DEFINE_STATIC_LOCAL(const Vector<RefPtr<InsertionPoint> >, emptyList, ()); if (m_shadowRootRareData && m_descendantInsertionPointsIsValid) return m_shadowRootRareData->descendantInsertionPoints(); m_descendantInsertionPointsIsValid = true; if (!containsInsertionPoints()) return emptyList; Vector<RefPtr<InsertionPoint> > insertionPoints; for (Element* element = ElementTraversal::firstWithin(*this); element; element = ElementTraversal::next(*element, this)) { if (element->isInsertionPoint()) insertionPoints.append(toInsertionPoint(element)); } ensureShadowRootRareData()->setDescendantInsertionPoints(insertionPoints); return m_shadowRootRareData->descendantInsertionPoints(); }