void RelatedNodeRetargeter::moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope) { if (m_hasDifferentTreeRoot) return; auto& currentRelatedNodeScope = m_retargetedRelatedNode->treeScope(); if (previousTreeScope != ¤tRelatedNodeScope) { // currentRelatedNode is still outside our shadow tree. New tree scope may contain currentRelatedNode // but there is no need to re-target it. Moving into a slot (thereby a deeper shadow tree) doesn't matter. return; } bool enteredSlot = newTreeScope.parentTreeScope() == previousTreeScope; if (enteredSlot) { if (m_lowestCommonAncestorIndex) { if (m_ancestorTreeScopes.isEmpty()) collectTreeScopes(); bool relatedNodeIsInSlot = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1] == &newTreeScope; if (relatedNodeIsInSlot) { m_lowestCommonAncestorIndex--; m_retargetedRelatedNode = nodeInLowestCommonAncestor(); ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope()); } } else ASSERT(m_retargetedRelatedNode == &m_relatedNode); } else { ASSERT(previousTreeScope->parentTreeScope() == &newTreeScope); m_lowestCommonAncestorIndex++; ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.isEmpty() || m_lowestCommonAncestorIndex < m_ancestorTreeScopes.size()); m_retargetedRelatedNode = downcast<ShadowRoot>(currentRelatedNodeScope.rootNode()).host(); ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope()); } }
Node* FocusController::findFocusableNodeAcrossTreeScope(FocusDirection direction, TreeScope* scope, Node* currentNode, KeyboardEvent* event) { Node* node = findFocusableNode(direction, scope, currentNode, event); // If there's no focusable node to advance to, move up the tree scopes until we find one. while (!node && scope) { Node* owner = ownerOfTreeScope(scope); if (!owner) break; node = findFocusableNode(direction, owner->treeScope(), owner, event); scope = owner->treeScope(); } node = deepFocusableNode(direction, node, event); return node; }
EventPath::EventPath(Node& originalTarget, Event& event) : m_event(event) { bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent(); #if ENABLE(TOUCH_EVENTS) bool isTouchEvent = event.isTouchEvent(); #endif Node* node = nodeOrHostIfPseudoElement(&originalTarget); Node* target = node ? eventTargetRespectingTargetRules(*node) : nullptr; while (node) { while (node) { EventTarget* currentTarget = eventTargetRespectingTargetRules(*node); if (isMouseOrFocusEvent) m_path.append(std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target)); #if ENABLE(TOUCH_EVENTS) else if (isTouchEvent) m_path.append(std::make_unique<TouchEventContext>(node, currentTarget, target)); #endif else m_path.append(std::make_unique<EventContext>(node, currentTarget, target)); if (is<ShadowRoot>(*node)) break; ContainerNode* parent = node->parentNode(); if (!parent) return; #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT) if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) { if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) { // node is assigned to a slot. Continue dispatching the event at this slot. parent = assignedSlot; } } #endif node = parent; } bool exitingShadowTreeOfTarget = &target->treeScope() == &node->treeScope(); ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node); if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget)) return; node = shadowRoot.host(); if (exitingShadowTreeOfTarget) target = eventTargetRespectingTargetRules(*node); } }
void EventPath::calculateAdjustedTargets() { const TreeScope* lastTreeScope = 0; bool useDeprecatedSVGUseTreeEventRules = usesDeprecatedSVGUseTreeEventRules(at(0).node()); TreeScopeEventContextMap treeScopeEventContextMap; TreeScopeEventContext* lastTreeScopeEventContext = 0; for (size_t i = 0; i < size(); ++i) { Node* currentNode = at(i).node(); TreeScope& currentTreeScope = currentNode->treeScope(); if (lastTreeScope != ¤tTreeScope) { if (!useDeprecatedSVGUseTreeEventRules) { lastTreeScopeEventContext = ensureTreeScopeEventContext(currentNode, ¤tTreeScope, treeScopeEventContextMap); } else { TreeScopeEventContextMap::AddResult addResult = treeScopeEventContextMap.add(¤tTreeScope, TreeScopeEventContext::create(currentTreeScope)); lastTreeScopeEventContext = addResult.storedValue->value.get(); if (addResult.isNewEntry) { // Don't adjust an event target for SVG. lastTreeScopeEventContext->setTarget(eventTargetRespectingTargetRules(at(0).node())); } } } ASSERT(lastTreeScopeEventContext); at(i).setTreeScopeEventContext(lastTreeScopeEventContext); lastTreeScope = ¤tTreeScope; } m_treeScopeEventContexts.appendRange(treeScopeEventContextMap.values().begin(), treeScopeEventContextMap.values().end()); }
static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) { Node* targetNode = target.toNode(); #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) // Video-only full screen is a mode where we use the shadow DOM as an implementation // detail that should not be detectable by the web content. if (targetNode) { if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { // FIXME: We assume that if the full screen element is a media element that it's // the video-only full screen. Both here and elsewhere. But that is probably wrong. if (element->isMediaElement() && shadowRoot.hostElement() == element) return false; } } #endif // WebKit never allowed selectstart event to cross the the shadow DOM boundary. // Changing this breaks existing sites. // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. const AtomicString& eventType = event.type(); bool targetIsInShadowRoot = targetNode && targetNode->treeScope().rootNode() == &shadowRoot; return !targetIsInShadowRoot || !(eventType == eventNames().abortEvent || eventType == eventNames().changeEvent || eventType == eventNames().errorEvent || eventType == eventNames().loadEvent || eventType == eventNames().resetEvent || eventType == eventNames().resizeEvent || eventType == eventNames().scrollEvent || eventType == eventNames().selectEvent || eventType == eventNames().selectstartEvent); }
static Node* moveOutOfAllShadowRoots(Node& startingNode) { Node* node = &startingNode; while (node->isInShadowTree()) node = downcast<ShadowRoot>(node->treeScope().rootNode()).host(); return node; }
Node* LiveNodeList::namedItem(const AtomicString& elementId) const { Node* rootNode = this->rootNode(); if (rootNode->inDocument()) { #if ENABLE(MICRODATA) if (type() == PropertyNodeListType) static_cast<const PropertyNodeList*>(this)->updateRefElements(); #endif Element* element = rootNode->treeScope()->getElementById(elementId); if (element && nodeMatches(element) && element->isDescendantOf(rootNode)) return element; if (!element) return 0; // In the case of multiple nodes with the same name, just fall through. } unsigned length = this->length(); for (unsigned i = 0; i < length; i++) { Node* node = item(i); // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. if (node->hasID() && toElement(node)->idForStyleResolution() == elementId) return node; } return 0; }
void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget) { Node* relatedNode = relatedTarget.toNode(); if (!relatedNode) return; EventRelatedNodeResolver resolver(*relatedNode); bool originIsRelatedTarget = &origin == relatedNode; Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode(); size_t eventPathSize = m_path.size(); size_t i = 0; while (i < eventPathSize) { Node* contextNode = m_path[i]->node(); Node* currentRelatedNode = resolver.moveToParentOrShadowHost(*contextNode); if (!originIsRelatedTarget && m_path[i]->target() == currentRelatedNode) break; toMouseOrFocusEventContext(*m_path[i]).setRelatedTarget(currentRelatedNode); i++; if (originIsRelatedTarget && &rootNodeInOriginTreeScope == contextNode) break; } m_path.shrink(i); }
Node* LiveNodeList::namedItem(const AtomicString& elementId) const { Node* rootNode = this->rootNode(); if (rootNode->inDocument()) { Element* element = rootNode->treeScope()->getElementById(elementId); if (element && nodeMatches(element) && element->isDescendantOf(rootNode)) return element; if (!element) return 0; // In the case of multiple nodes with the same name, just fall through. } unsigned length = this->length(); for (unsigned i = 0; i < length; i++) { Node* node = item(i); if (!node->isElementNode()) continue; Element* element = toElement(node); // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. if (element->hasID() && element->idForStyleResolution() == elementId) return node; } return 0; }
void SelectorDataList::executeQueryAll(Node& rootNode, Vector<RefPtr<Node> >& matchedElements) const { if (!canUseFastQuery(rootNode)) return executeSlowQueryAll(rootNode, matchedElements); ASSERT(m_selectors.size() == 1); ASSERT(m_selectors[0].selector); const CSSSelector* firstSelector = m_selectors[0].selector; if (!firstSelector->tagHistory()) { // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and querySelectorAll('div'). switch (firstSelector->m_match) { case CSSSelector::Id: { if (rootNode.document().containsMultipleElementsWithId(firstSelector->value())) break; // Just the same as getElementById. Element* element = rootNode.treeScope().getElementById(firstSelector->value()); if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) matchedElements.append(element); return; } case CSSSelector::Class: return collectElementsByClassName(rootNode, firstSelector->value(), matchedElements); case CSSSelector::Tag: return collectElementsByTagName(rootNode, firstSelector->tagQName(), matchedElements); default: break; // If we need another fast path, add here. } } bool matchTraverseRoots; OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(&rootNode, matchTraverseRoots); if (traverseRoots->isEmpty()) return; const SelectorData& selector = m_selectors[0]; if (matchTraverseRoots) { while (!traverseRoots->isEmpty()) { Node& node = *traverseRoots->next(); Element& element = toElement(node); if (selectorMatches(selector, element, rootNode)) matchedElements.append(&element); } return; } while (!traverseRoots->isEmpty()) { Node* traverseRoot = traverseRoots->next(); for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) { if (selectorMatches(selector, *element, rootNode)) matchedElements.append(element); } } }
FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(Node* node) { ASSERT(node); Node* root = node; for (Node* n = node; n; n = n->parentNode()) root = n; // The result is not always a ShadowRoot nor a DocumentNode since // a starting node is in an orphaned tree in composed shadow tree. return FocusNavigationScope(&root->treeScope()); }
inline void TreeScopeAdopter::updateTreeScope(Node& node) const { ASSERT(!node.isTreeScope()); ASSERT(node.treeScope() == oldScope()); #if !ENABLE(OILPAN) newScope().guardRef(); oldScope().guardDeref(); #endif node.setTreeScope(m_newScope); }
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); } }
EventRelatedNodeResolver(Node& relatedNode) : m_relatedNode(relatedNode) , m_relatedNodeTreeScope(relatedNode.treeScope()) , m_relatedNodeInCurrentTreeScope(nullptr) , m_currentTreeScope(nullptr) #if ENABLE(TOUCH_EVENTS) , m_touch(0) , m_touchListType(TouchEventContext::NotTouchList) #endif { }
RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, Node& target) : m_relatedNode(relatedNode) , m_retargetedRelatedNode(&relatedNode) { auto& targetTreeScope = target.treeScope(); TreeScope* currentTreeScope = &m_relatedNode.treeScope(); if (LIKELY(currentTreeScope == &targetTreeScope && target.inDocument() && m_relatedNode.inDocument())) return; if (¤tTreeScope->documentScope() != &targetTreeScope.documentScope()) { m_hasDifferentTreeRoot = true; m_retargetedRelatedNode = nullptr; return; } if (relatedNode.inDocument() != target.inDocument()) { m_hasDifferentTreeRoot = true; m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode); return; } collectTreeScopes(); // FIXME: We should collect this while constructing the event path. Vector<TreeScope*, 8> targetTreeScopeAncestors; for (TreeScope* currentTreeScope = &targetTreeScope; currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope()) targetTreeScopeAncestors.append(currentTreeScope); ASSERT_WITH_SECURITY_IMPLICATION(!targetTreeScopeAncestors.isEmpty()); unsigned i = m_ancestorTreeScopes.size(); unsigned j = targetTreeScopeAncestors.size(); ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.last() == targetTreeScopeAncestors.last()); while (m_ancestorTreeScopes[i - 1] == targetTreeScopeAncestors[j - 1]) { i--; j--; if (!i || !j) break; } bool lowestCommonAncestorIsDocumentScope = i + 1 == m_ancestorTreeScopes.size(); if (lowestCommonAncestorIsDocumentScope && !relatedNode.inDocument() && !target.inDocument()) { Node& targetAncestorInDocumentScope = i ? *downcast<ShadowRoot>(m_ancestorTreeScopes[i - 1]->rootNode()).shadowHost() : target; Node& relatedNodeAncestorInDocumentScope = j ? *downcast<ShadowRoot>(targetTreeScopeAncestors[j - 1]->rootNode()).shadowHost() : relatedNode; if (targetAncestorInDocumentScope.rootNode() != relatedNodeAncestorInDocumentScope.rootNode()) { m_hasDifferentTreeRoot = true; m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode); return; } } m_lowestCommonAncestorIndex = i; m_retargetedRelatedNode = nodeInLowestCommonAncestor(); }
FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(Node* node) { ASSERT(node); ComposedShadowTreeWalker walker(node, ComposedShadowTreeWalker::DoNotCrossUpperBoundary); Node* root = node; while (walker.get()) { root = walker.get(); walker.parent(); } // The result is not always a ShadowRoot nor a DocumentNode since // a starting node is in an orphaned tree in composed shadow tree. return FocusNavigationScope(root->treeScope()); }
void StyleEngine::modifiedStyleSheet(StyleSheet* sheet) { if (!sheet) return; Node* node = sheet->ownerNode(); if (!node || !node->inDocument()) return; TreeScope& treeScope = node->hasTagName(styleTag) ? node->treeScope() : m_document; ASSERT(node->hasTagName(styleTag) || treeScope == m_document); markTreeScopeDirty(treeScope); }
void StyleEngine::modifiedStyleSheet(StyleSheet* sheet) { if (!sheet) return; Node* node = sheet->ownerNode(); if (!node || !node->inDocument()) return; TreeScope& treeScope = isHTMLStyleElement(*node) ? node->treeScope() : *m_document; ASSERT(isHTMLStyleElement(node) || treeScope == m_document); markTreeScopeDirty(treeScope); }
void EventRelatedTargetAdjuster::adjust(Vector<EventContext, 32>& ancestors) { // Synthetic mouse events can have a relatedTarget which is identical to the target. bool targetIsIdenticalToToRelatedTarget = (m_node.get() == m_relatedTarget.get()); Vector<EventTarget*, 32> relatedTargetStack; TreeScope* lastTreeScope = 0; for (AncestorChainWalker walker(m_relatedTarget.get()); walker.get(); walker.parent()) { Node* node = walker.get(); if (relatedTargetStack.isEmpty()) relatedTargetStack.append(node); else if (walker.crossingInsertionPoint()) 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; if (node->isShadowRoot()) { ASSERT(!relatedTargetStack.isEmpty()); relatedTargetStack.removeLast(); } } lastTreeScope = 0; EventTarget* adjustedRelatedTarget = 0; for (Vector<EventContext, 32>::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 (targetIsIdenticalToToRelatedTarget) { if (m_node->treeScope()->rootNode() == iter->node()) { ancestors.shrink(iter + 1 - ancestors.begin()); break; } } else if (iter->target() == adjustedRelatedTarget) { // Event dispatching should be stopped here. ancestors.shrink(iter - ancestors.begin()); break; } } }
Element* HTMLDocument::activeElement() { Node* node = focusedNode(); if (!node && page()) node = focusedFrameOwnerElement(page()->focusController()->focusedFrame(), frame()); if (!node) return body(); ASSERT(node->document() == this); while (node->treeScope() != this) { node = node->parentOrHostNode(); ASSERT(node); } if (node->isElementNode()) return toElement(node); return body(); }
static Node* findHostOfTreeScopeInTargetTreeScope(const TreeScope& startingTreeScope, const TreeScope& targetScope) { ASSERT(&targetScope != &startingTreeScope); Node* previousHost = nullptr; for (const TreeScope* scope = &startingTreeScope; scope; scope = scope->parentTreeScope()) { if (scope == &targetScope) { ASSERT(previousHost); ASSERT_WITH_SECURITY_IMPLICATION(&previousHost->treeScope() == &targetScope); return previousHost; } if (is<ShadowRoot>(scope->rootNode())) previousHost = downcast<ShadowRoot>(scope->rootNode()).host(); else ASSERT_WITH_SECURITY_IMPLICATION(!scope->parentTreeScope()); } return nullptr; }
void EventRetargeter::buildRelatedNodeMap(const Node* relatedNode, RelatedNodeMap& relatedNodeMap) { Vector<Node*, 32> relatedNodeStack; TreeScope* lastTreeScope = 0; for (Node* node = nodeOrHostIfPseudoElement(const_cast<Node*>(relatedNode)); node; node = node->parentOrShadowHostNode()) { if (relatedNodeStack.isEmpty()) relatedNodeStack.append(node); TreeScope* scope = node->treeScope(); // Skips adding a node to the map if treeScope does not change. Just for the performance optimization. if (scope != lastTreeScope) relatedNodeMap.add(scope, relatedNodeStack.last()); lastTreeScope = scope; if (node->isShadowRoot()) { ASSERT(!relatedNodeStack.isEmpty()); relatedNodeStack.removeLast(); } } }
void EventPath::shrinkIfNeeded(const Node& target, const EventTarget& relatedTarget) { // Synthetic mouse events can have a relatedTarget which is identical to the target. bool targetIsIdenticalToToRelatedTarget = (&target == &relatedTarget); for (size_t i = 0; i < size(); ++i) { if (targetIsIdenticalToToRelatedTarget) { if (target.treeScope().rootNode() == at(i).node()) { shrink(i + 1); break; } } else if (at(i).target() == at(i).relatedTarget()) { // Event dispatching should be stopped here. shrink(i); break; } } }
void EventPath::calculateAdjustedTargets() { const TreeScope* lastTreeScope = 0; TreeScopeEventContextMap treeScopeEventContextMap; TreeScopeEventContext* lastTreeScopeEventContext = 0; for (size_t i = 0; i < size(); ++i) { Node* currentNode = at(i).node(); TreeScope& currentTreeScope = currentNode->treeScope(); if (lastTreeScope != ¤tTreeScope) { lastTreeScopeEventContext = ensureTreeScopeEventContext(currentNode, ¤tTreeScope, treeScopeEventContextMap); } ASSERT(lastTreeScopeEventContext); at(i).setTreeScopeEventContext(lastTreeScopeEventContext); lastTreeScope = ¤tTreeScope; } m_treeScopeEventContexts.appendRange(treeScopeEventContextMap.values().begin(), treeScopeEventContextMap.values().end()); }
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; } } }
static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) { Node* targetNode = target.toNode(); #if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) // Video-only full screen is a mode where we use the shadow DOM as an implementation // detail that should not be detectable by the web content. if (targetNode) { if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { // FIXME: We assume that if the full screen element is a media element that it's // the video-only full screen. Both here and elsewhere. But that is probably wrong. if (element->isMediaElement() && shadowRoot.host() == element) return false; } } #endif bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot; return !targetIsInShadowRoot || !event.scoped(); }
void EventRetargeter::buildRelatedNodeMap(const Node* relatedNode, RelatedNodeMap& relatedNodeMap) { Vector<Node*, 32> relatedNodeStack; TreeScope* lastTreeScope = 0; for (EventPathWalker walker(relatedNode); walker.node(); walker.moveToParent()) { Node* node = walker.node(); if (relatedNodeStack.isEmpty()) relatedNodeStack.append(node); else if (walker.isVisitingInsertionPointInReprojection()) relatedNodeStack.append(relatedNodeStack.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) relatedNodeMap.add(scope, relatedNodeStack.last()); lastTreeScope = scope; if (node->isShadowRoot()) { ASSERT(!relatedNodeStack.isEmpty()); relatedNodeStack.removeLast(); } } }
void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget) { UNUSED_PARAM(origin); Node* relatedNode = relatedTarget.toNode(); if (!relatedNode || m_path.isEmpty()) return; RelatedNodeRetargeter retargeter(*relatedNode, downcast<MouseOrFocusEventContext>(*m_path[0]).node()->treeScope()); bool originIsRelatedTarget = &origin == relatedNode; // FIXME: We should add a new flag on Event instead. bool shouldTrimEventPath = m_event.type() == eventNames().mouseoverEvent || m_event.type() == eventNames().mousemoveEvent || m_event.type() == eventNames().mouseoutEvent; Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode(); TreeScope* previousTreeScope = nullptr; size_t originalEventPathSize = m_path.size(); for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) { auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]); TreeScope& currentTreeScope = context.node()->treeScope(); if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope)) retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope); Node* currentRelatedNode = retargeter.currentNode(currentTreeScope); if (UNLIKELY(shouldTrimEventPath && !originIsRelatedTarget && context.target() == currentRelatedNode)) { m_path.shrink(contextIndex); break; } context.setRelatedTarget(currentRelatedNode); if (UNLIKELY(shouldTrimEventPath && originIsRelatedTarget && context.node() == &rootNodeInOriginTreeScope)) { m_path.shrink(contextIndex + 1); break; } previousTreeScope = ¤tTreeScope; } }
void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget) { Node* relatedNode = relatedTarget.toNode(); if (!relatedNode || m_path.isEmpty()) return; RelatedNodeRetargeter retargeter(*relatedNode, *m_path[0]->node()); bool originIsRelatedTarget = &origin == relatedNode; bool relatedTargetScoped = m_event.relatedTargetScoped(); Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode(); TreeScope* previousTreeScope = nullptr; size_t originalEventPathSize = m_path.size(); for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) { auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]); Node& currentTarget = *context.node(); TreeScope& currentTreeScope = currentTarget.treeScope(); if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope)) retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope); Node* currentRelatedNode = retargeter.currentNode(currentTarget); if (UNLIKELY(relatedTargetScoped && !originIsRelatedTarget && context.target() == currentRelatedNode)) { m_path.shrink(contextIndex); break; } context.setRelatedTarget(currentRelatedNode); if (UNLIKELY(relatedTargetScoped && originIsRelatedTarget && context.node() == &rootNodeInOriginTreeScope)) { m_path.shrink(contextIndex + 1); break; } previousTreeScope = ¤tTreeScope; } }
bool ValidationMessage::shadowTreeContains(const Node& node) const { if (validationMessageClient() || !m_bubble) return false; return &m_bubble->treeScope() == &node.treeScope(); }