EventPath::EventPath(Node& targetNode, Event& event) { bool inDocument = targetNode.inDocument(); bool isSVGElement = targetNode.isSVGElement(); bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent(); #if ENABLE(TOUCH_EVENTS) bool isTouchEvent = event.isTouchEvent(); #endif EventTarget* target = 0; Node* node = nodeOrHostIfPseudoElement(&targetNode); while (node) { if (!target || !isSVGElement) // FIXME: This code doesn't make sense once we've climbed out of the SVG subtree in a HTML document. target = &eventTargetRespectingTargetRules(*node); for (; node; node = node->parentNode()) { EventTarget& currentTarget = eventTargetRespectingTargetRules(*node); if (isMouseOrFocusEvent) m_path.append(std::make_unique<MouseOrFocusEventContext>(node, ¤tTarget, target)); #if ENABLE(TOUCH_EVENTS) else if (isTouchEvent) m_path.append(std::make_unique<TouchEventContext>(node, ¤tTarget, target)); #endif else m_path.append(std::make_unique<EventContext>(node, ¤tTarget, target)); if (!inDocument) return; if (node->isShadowRoot()) break; } if (!node || !shouldEventCrossShadowBoundary(event, *toShadowRoot(node), *target)) return; node = toShadowRoot(node)->hostElement(); } }
inline void TreeScopeAdopter::moveNodeToNewDocument( Node& node, Document& oldDocument, Document& newDocument) const { DCHECK_NE(oldDocument, newDocument); if (node.hasRareData()) { NodeRareData* rareData = node.rareData(); if (rareData->nodeLists()) rareData->nodeLists()->adoptDocument(oldDocument, newDocument); } oldDocument.moveNodeIteratorsToNewDocument(node, newDocument); if (node.getCustomElementState() == CustomElementState::Custom) { Element& element = toElement(node); CustomElement::enqueueAdoptedCallback(&element, &oldDocument, &newDocument); } if (node.isShadowRoot()) toShadowRoot(node).setDocument(newDocument); #if DCHECK_IS_ON() didMoveToNewDocumentWasCalled = false; oldDocumentDidMoveToNewDocumentWasCalledWith = &oldDocument; #endif node.didMoveToNewDocument(oldDocument); #if DCHECK_IS_ON() DCHECK(didMoveToNewDocumentWasCalled); #endif }
void EventDispatcher::ensureEventAncestors(Event* event) { if (m_ancestorsInitialized) return; m_ancestorsInitialized = true; bool inDocument = m_node->inDocument(); bool isSVGElement = m_node->isSVGElement(); Vector<EventTarget*, 32> targetStack; for (AncestorChainWalker walker(m_node.get()); walker.get(); walker.parent()) { Node* node = walker.get(); if (targetStack.isEmpty()) targetStack.append(eventTargetRespectingTargetRules(node)); else if (walker.crossingInsertionPoint()) targetStack.append(targetStack.last()); m_ancestors.append(EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last())); if (!inDocument) return; if (!node->isShadowRoot()) continue; if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) return; if (!isSVGElement) { ASSERT(!targetStack.isEmpty()); targetStack.removeLast(); } } }
void EventPathWalker::moveToParent() { ASSERT(m_node); ASSERT(m_distributedNode); if (ElementShadow* shadow = shadowOfParent(m_node)) { ContentDistributor::ensureDistribution(shadow->youngestShadowRoot()); if (InsertionPoint* insertionPoint = shadow->distributor().findInsertionPointFor(m_distributedNode)) { m_node = insertionPoint; m_isVisitingInsertionPointInReprojection = true; return; } } if (!m_node->isShadowRoot()) { m_node = m_node->parentNode(); if (!(m_node && m_node->isShadowRoot() && ScopeContentDistribution::assignedTo(toShadowRoot(m_node)))) m_distributedNode = m_node; m_isVisitingInsertionPointInReprojection = false; return; } const ShadowRoot* shadowRoot = toShadowRoot(m_node); if (InsertionPoint* insertionPoint = ScopeContentDistribution::assignedTo(shadowRoot)) { m_node = insertionPoint; m_isVisitingInsertionPointInReprojection = true; return; } m_node = shadowRoot->host(); m_distributedNode = m_node; m_isVisitingInsertionPointInReprojection = false; }
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 EventRetargeter::calculateEventPath(Node* targetNode, Event* event, EventPath& eventPath) { bool inDocument = targetNode->inDocument(); bool isSVGElement = targetNode->isSVGElement(); bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent(); #if ENABLE(TOUCH_EVENTS) bool isTouchEvent = event->isTouchEvent(); #endif Vector<EventTarget*, 32> targetStack; for (Node* node = nodeOrHostIfPseudoElement(targetNode); node; node = node->parentOrShadowHostNode()) { if (targetStack.isEmpty()) targetStack.append(eventTargetRespectingTargetRules(node)); if (isMouseOrFocusEvent) eventPath.append(adoptPtr(new MouseOrFocusEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); #if ENABLE(TOUCH_EVENTS) else if (isTouchEvent) eventPath.append(adoptPtr(new TouchEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); #endif else eventPath.append(adoptPtr(new EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); if (!inDocument) return; if (!node->isShadowRoot()) continue; if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) return; if (!isSVGElement) { ASSERT(!targetStack.isEmpty()); targetStack.removeLast(); } } }
void EventRetargeter::calculateEventPath(Node* node, Event* event) { EventPath& eventPath = event->eventPath(); eventPath.clear(); bool inDocument = node->inDocument(); bool isSVGElement = node->isSVGElement(); bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent(); bool isTouchEvent = event->isTouchEvent(); Vector<EventTarget*, 32> targetStack; for (EventPathWalker walker(node); walker.node(); walker.moveToParent()) { Node* node = walker.node(); if (targetStack.isEmpty()) targetStack.append(eventTargetRespectingTargetRules(node)); else if (walker.isVisitingInsertionPointInReprojection()) targetStack.append(targetStack.last()); if (isMouseOrFocusEvent) eventPath.append(adoptPtr(new MouseOrFocusEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); else if (isTouchEvent) eventPath.append(adoptPtr(new TouchEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); else eventPath.append(adoptPtr(new EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); if (!inDocument) return; if (!node->isShadowRoot()) continue; if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) return; if (!isSVGElement) { ASSERT(!targetStack.isEmpty()); targetStack.removeLast(); } } }
inline EventTarget& eventTargetRespectingTargetRules(Node& referenceNode) { if (referenceNode.isPseudoElement()) { EventTarget* hostElement = toPseudoElement(referenceNode).hostElement(); ASSERT(hostElement); return *hostElement; } #if ENABLE(SVG) if (!referenceNode.isSVGElement() || !referenceNode.isInShadowTree()) return referenceNode; // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects Node* rootNode = referenceNode.treeScope().rootNode(); Element* shadowHostElement = rootNode->isShadowRoot() ? toShadowRoot(rootNode)->hostElement() : 0; // At this time, SVG nodes are not supported in non-<use> shadow trees. if (!shadowHostElement || !shadowHostElement->hasTagName(SVGNames::useTag)) return referenceNode; SVGUseElement* useElement = toSVGUseElement(shadowHostElement); if (SVGElementInstance* instance = useElement->instanceForShadowTreeElement(&referenceNode)) return *instance; #endif return referenceNode; }
void AncestorChainWalker::parent() { ASSERT(m_node); ASSERT(m_distributedNode); if (ElementShadow* shadow = shadowOfParent(m_node)) { if (InsertionPoint* insertionPoint = shadow->insertionPointFor(m_distributedNode)) { m_node = insertionPoint; m_isCrossingInsertionPoint = true; return; } } if (!m_node->isShadowRoot()) { m_node = m_node->parentNode(); m_distributedNode = m_node; m_isCrossingInsertionPoint = false; return; } const ShadowRoot* shadowRoot = toShadowRoot(m_node); if (InsertionPoint* insertionPoint = shadowRoot->assignedTo()) { m_node = insertionPoint; m_isCrossingInsertionPoint = true; return; } m_node = shadowRoot->host(); m_distributedNode = m_node; m_isCrossingInsertionPoint = false; }
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 SVGShadowText::willRecalcTextStyle(StyleChange change) { if (change != NoChange && parentNode()->isShadowRoot()) { if (renderer()) renderer()->setStyle(toShadowRoot(parentNode())->host()->renderer()->style()); } }
// TODO(hayato): This may return a wrong result for a node which is not in a // document flat tree. See FlatTreeTraversalTest's redistribution test for details. Node* FlatTreeTraversal::traverseSiblings(const Node& node, TraversalDirection direction) { if (node.isChildOfV1ShadowHost()) return traverseSiblingsForV1HostChild(node, direction); if (shadowWhereNodeCanBeDistributed(node)) return traverseSiblingsForV0Distribution(node, direction); if (Node* found = resolveDistributionStartingAt(direction == TraversalDirectionForward ? node.nextSibling() : node.previousSibling(), direction)) return found; if (!node.isInV0ShadowTree()) return nullptr; // For v0 older shadow tree if (node.parentNode() && node.parentNode()->isShadowRoot()) { ShadowRoot* parentShadowRoot = toShadowRoot(node.parentNode()); if (!parentShadowRoot->isYoungest()) { HTMLShadowElement* assignedInsertionPoint = parentShadowRoot->shadowInsertionPointOfYoungerShadowRoot(); DCHECK(assignedInsertionPoint); return traverseSiblings(*assignedInsertionPoint, direction); } } return nullptr; }
String SVGElement::title() const { // According to spec, we should not return titles when hovering over root <svg> elements (those // <title> elements are the title of the document, not a tooltip) so we instantly return. if (isOutermostSVGSVGElement()) return String(); // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. if (isInShadowTree()) { Element* shadowHostElement = toShadowRoot(treeScope().rootNode()).host(); // At this time, SVG nodes are not allowed in non-<use> shadow trees, so any shadow root we do // have should be a use. The assert and following test is here to catch future shadow DOM changes // that do enable SVG in a shadow tree. ASSERT(!shadowHostElement || isSVGUseElement(*shadowHostElement)); if (isSVGUseElement(shadowHostElement)) { SVGUseElement& useElement = toSVGUseElement(*shadowHostElement); // If the <use> title is not empty we found the title to use. String useTitle(useElement.title()); if (!useTitle.isEmpty()) return useTitle; } } // If we aren't an instance in a <use> or the <use> title was not found, then find the first // <title> child of this element. // If a title child was found, return the text contents. if (Element* titleElement = Traversal<SVGTitleElement>::firstChild(*this)) return titleElement->innerText(); // Otherwise return a null/empty string. return String(); }
Element* Internals::getElementByIdInShadowRoot(Node* shadowRoot, const String& id, ExceptionCode& ec) { if (!shadowRoot || !shadowRoot->isShadowRoot()) { ec = INVALID_ACCESS_ERR; return 0; } return toShadowRoot(shadowRoot)->getElementById(id); }
TreeScope* TreeScope::olderShadowRootOrParentTreeScope() const { if (rootNode().isShadowRoot()) { if (ShadowRoot* olderShadowRoot = toShadowRoot(rootNode()).olderShadowRoot()) return olderShadowRoot; } return parentTreeScope(); }
bool HTMLShadowElement::doesSelectFromHostChildren() const { TreeScope* scope = treeScope(); if (scope->isShadowRoot()) return toShadowRoot(scope)->isOldest(); return false; }
Internals::ShadowRootIfShadowDOMEnabledOrNode* Internals::olderShadowRoot(Node* shadow, ExceptionCode& ec) { if (!shadow || !shadow->isShadowRoot()) { ec = INVALID_ACCESS_ERR; return 0; } return toShadowRoot(shadow)->olderShadowRoot(); }
void EventPath::calculatePath() { ASSERT(m_node); ASSERT(m_nodeEventContexts.isEmpty()); m_node->updateDistribution(); // For performance and memory usage reasons we want to store the // path using as few bytes as possible and with as few allocations // as possible which is why we gather the data on the stack before // storing it in a perfectly sized m_nodeEventContexts Vector. WillBeHeapVector<RawPtrWillBeMember<Node>, 64> nodesInPath; Node* current = m_node; nodesInPath.append(current); while (current) { if (m_event && current->keepEventInNode(m_event)) break; WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; collectDestinationInsertionPoints(*current, insertionPoints); if (!insertionPoints.isEmpty()) { for (const auto& insertionPoint : insertionPoints) { if (insertionPoint->isShadowInsertionPoint()) { ShadowRoot* containingShadowRoot = insertionPoint->containingShadowRoot(); ASSERT(containingShadowRoot); if (!containingShadowRoot->isOldest()) nodesInPath.append(containingShadowRoot->olderShadowRoot()); } nodesInPath.append(insertionPoint); } current = insertionPoints.last(); continue; } if (current->isShadowRoot()) { if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(current), *m_node)) break; current = current->shadowHost(); #if !ENABLE(OILPAN) // TODO(kochi): crbug.com/507413 This check is necessary when some asynchronous event // is queued while its shadow host is removed and the shadow root gets the event // immediately after it. When Oilpan is enabled, this situation does not happen. // Except this case, shadow root's host is assumed to be non-null. if (current) nodesInPath.append(current); #else nodesInPath.append(current); #endif } else { current = current->parentNode(); if (current) nodesInPath.append(current); } } m_nodeEventContexts.reserveCapacity(nodesInPath.size()); for (Node* nodeInPath : nodesInPath) { m_nodeEventContexts.append(NodeEventContext(nodeInPath, eventTargetRespectingTargetRules(*nodeInPath))); } }
Element* FocusNavigationScope::owner() const { Node* root = rootNode(); if (root->isShadowRoot()) { ShadowRoot* shadowRoot = toShadowRoot(root); return shadowRoot->host(); } return 0; }
TreeScopeStyleSheetCollection* StyleEngine::ensureStyleSheetCollectionFor(TreeScope& treeScope) { if (treeScope == m_document) return documentStyleSheetCollection(); StyleSheetCollectionMap::AddResult result = m_styleSheetCollectionMap.add(&treeScope, nullptr); if (result.isNewEntry) result.storedValue->value = adoptPtr(new ShadowTreeStyleSheetCollection(toShadowRoot(treeScope))); return result.storedValue->value.get(); }
int TreeScopeEventContext::calculateTreeOrderAndSetNearestAncestorClosedTree(int orderNumber, TreeScopeEventContext* nearestAncestorClosedTreeScopeEventContext) { m_preOrder = orderNumber; m_containingClosedShadowTree = (rootNode().isShadowRoot() && !toShadowRoot(rootNode()).isOpenOrV0()) ? this : nearestAncestorClosedTreeScopeEventContext; for (size_t i = 0; i < m_children.size(); ++i) orderNumber = m_children[i]->calculateTreeOrderAndSetNearestAncestorClosedTree(orderNumber + 1, containingClosedShadowTree()); m_postOrder = orderNumber + 1; return orderNumber + 1; }
void EventRetargeter::calculateEventPath(Node* node, Event* event) { EventPath& eventPath = event->eventPath(); eventPath.clear(); bool inDocument = node->inDocument(); bool isSVGElement = node->isSVGElement(); bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent(); bool isTouchEvent = event->isTouchEvent(); Vector<EventTarget*, 32> targetStack; for (EventPathWalker walker(node); walker.node(); walker.moveToParent()) { Node* node = walker.node(); if (targetStack.isEmpty()) targetStack.append(eventTargetRespectingTargetRules(node)); else if (walker.isVisitingInsertionPointInReprojection()) targetStack.append(targetStack.last()); if (isMouseOrFocusEvent) eventPath.append(adoptPtr(new MouseOrFocusEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); else if (isTouchEvent) eventPath.append(adoptPtr(new TouchEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); else eventPath.append(adoptPtr(new EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last()))); if (!inDocument) return; if (!node->isShadowRoot()) continue; if (determineDispatchBehavior(event, toShadowRoot(node), targetStack.last()) == StayInsideShadowDOM) return; if (!isSVGElement) { ASSERT(!targetStack.isEmpty()); targetStack.removeLast(); } } // Calculates eventPath for each node for Event.path() API. if (!RuntimeEnabledFeatures::experimentalShadowDOMEnabled()) return; TreeScope* lastScope = 0; size_t eventPathSize = eventPath.size(); for (size_t i = 0; i < eventPathSize; ++i) { TreeScope* currentScope = eventPath[i]->node()->treeScope(); if (currentScope == lastScope) { // Fast path. eventPath[i]->setEventPath(eventPath[i - 1]->eventPath()); continue; } lastScope = currentScope; Vector<RefPtr<Node> > nodes; for (size_t j = 0; j < eventPathSize; ++j) { Node* node = eventPath[j]->node(); if (node->treeScope()->isInclusiveAncestorOf(currentScope)) nodes.append(node); } eventPath[i]->adoptEventPath(nodes); } }
ShadowRoot* InsertionPoint::assignedFrom() const { Node* treeScopeRoot = treeScope()->rootNode(); if (!treeScopeRoot->isShadowRoot()) return 0; ShadowRoot* olderShadowRoot = toShadowRoot(treeScopeRoot)->olderShadowRoot(); if (olderShadowRoot && olderShadowRoot->assignedTo() == this) return olderShadowRoot; return 0; }
ShadowRoot* InsertionPoint::assignedFrom() const { TreeScope* scope = treeScope(); if (!scope->isShadowRoot()) return 0; ShadowRoot* olderShadowRoot = toShadowRoot(scope)->olderShadowRoot(); if (olderShadowRoot && olderShadowRoot->assignedTo() == this) return olderShadowRoot; return 0; }
void StyleScopeResolver::pop(const ContainerNode* scope) { // Only bother to update the scoping element stack if it is consistent. if (stackIsConsistent(scope)) { if (!m_stack.isEmpty() && m_stack.last().m_scope == scope) m_stack.removeLast(); if (scope->isShadowRoot() && !toShadowRoot(scope)->applyAuthorStyles()) --m_stackParentBoundsIndex; m_stackParent = scope->parentOrHostNode(); } }
Node* ComposedShadowTreeWalker::traverseParent(const Node* node) const { if (!canCrossUpperBoundary() && node->isShadowRoot()) { ASSERT(toShadowRoot(node)->isYoungest()); return 0; } if (ElementShadow* shadow = shadowOfParent(node)) { if (InsertionPoint* insertionPoint = shadow->insertionPointFor(node)) return traverseParent(insertionPoint); } return traverseParentInCurrentTree(node); }
inline Node* ComposedTreeWalker::traverseParentOrHost(const Node* node) const { Node* parent = node->parentNode(); if (!parent) return 0; if (!parent->isShadowRoot()) return parent; ShadowRoot* shadowRoot = toShadowRoot(parent); ASSERT(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); if (!shadowRoot->isYoungest()) return 0; return shadowRoot->host(); }
Node* ComposedShadowTreeWalker::traverseBackToYoungerShadowRoot(const Node* node, TraversalDirection direction) { ASSERT(node); if (node->parentNode() && node->parentNode()->isShadowRoot()) { ShadowRoot* parentShadowRoot = toShadowRoot(node->parentNode()); if (!parentShadowRoot->isYoungest()) { InsertionPoint* assignedInsertionPoint = parentShadowRoot->insertionPoint(); ASSERT(assignedInsertionPoint); return traverseSiblingInCurrentTree(assignedInsertionPoint, direction); } } return 0; }
ContainerNode* FlatTreeTraversal::traverseParentOrHost(const Node& node) { ContainerNode* parent = node.parentNode(); if (!parent) return nullptr; if (!parent->isShadowRoot()) return parent; ShadowRoot* shadowRoot = toShadowRoot(parent); DCHECK(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); if (!shadowRoot->isYoungest()) return nullptr; return &shadowRoot->host(); }
inline void TreeScopeAdopter::moveNodeToNewDocument(Node& node, Document& oldDocument, Document& newDocument) const { ASSERT(oldDocument != newDocument); if (node.isShadowRoot()) toShadowRoot(node).setDocument(newDocument); #if ENABLE(ASSERT) didMoveToNewDocumentWasCalled = false; oldDocumentDidMoveToNewDocumentWasCalledWith = &oldDocument; #endif node.didMoveToNewDocument(oldDocument); ASSERT(didMoveToNewDocumentWasCalled); }