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(); } } }
Element* TreeScope::adjustedFocusedElement() const { Document& document = rootNode().document(); Element* element = document.focusedElement(); if (!element && document.page()) element = document.page()->focusController().focusedFrameOwnerElement( *document.frame()); if (!element) return nullptr; if (rootNode().isInV1ShadowTree()) { if (Element* retargeted = retarget(*element)) { return (this == &retargeted->treeScope()) ? retargeted : nullptr; } return nullptr; } EventPath* eventPath = new EventPath(*element); for (size_t i = 0; i < eventPath->size(); ++i) { if (eventPath->at(i).node() == rootNode()) { // eventPath->at(i).target() is one of the followings: // - InsertionPoint // - shadow host // - Document::focusedElement() // So, it's safe to do toElement(). return toElement(eventPath->at(i).target()->toNode()); } } return nullptr; }
void EventRetargeter::calculateEventPath(Node* node, Event* event, EventPath& eventPath) { bool inDocument = node->inDocument(); bool isSVGElement = node->isSVGElement(); bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent(); #if ENABLE(TOUCH_EVENTS) bool isTouchEvent = event->isTouchEvent(); #endif 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()))); #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(); } } }
static void callDefaultEventHandlersInTheBubblingOrder(Event& event, const EventPath& path) { // Non-bubbling events call only one default event handler, the one for the target. path.contextAt(0).node()->defaultEventHandler(&event); ASSERT(!event.defaultPrevented()); if (event.defaultHandled() || !event.bubbles()) return; size_t size = path.size(); for (size_t i = 1; i < size; ++i) { path.contextAt(i).node()->defaultEventHandler(&event); ASSERT(!event.defaultPrevented()); if (event.defaultHandled()) return; } }
PassRefPtr<StaticNodeList> TreeScopeEventContext::ensureEventPath(EventPath& path) { if (m_eventPath) return m_eventPath; Vector<RefPtr<Node> > nodes; nodes.reserveInitialCapacity(path.size()); for (size_t i = 0; i < path.size(); ++i) { TreeScope& treeScope = path[i].treeScopeEventContext().treeScope(); if (treeScope.rootNode().isShadowRoot()) nodes.append(path[i].node()); else if (path[i].treeScopeEventContext().isInclusiveAncestorOf(*this)) nodes.append(path[i].node()); } m_eventPath = StaticNodeList::adopt(nodes); return m_eventPath; }
static void dispatchEventInDOM(Event& event, const EventPath& path, WindowEventContext& windowEventContext) { // Trigger capturing event handlers, starting at the top and working our way down. event.setEventPhase(Event::CAPTURING_PHASE); // We don't dispatch load events to the window. This quirk was originally // added because Mozilla doesn't propagate load events to the window object. bool shouldFireEventAtWindow = event.type() != eventNames().loadEvent; if (shouldFireEventAtWindow && windowEventContext.handleLocalEvents(event) && event.propagationStopped()) return; for (size_t i = path.size() - 1; i > 0; --i) { const EventContext& eventContext = path.contextAt(i); if (eventContext.currentTargetSameAsTarget()) continue; eventContext.handleLocalEvents(event); if (event.propagationStopped()) return; } event.setEventPhase(Event::AT_TARGET); path.contextAt(0).handleLocalEvents(event); if (event.propagationStopped()) return; // Trigger bubbling event handlers, starting at the bottom and working our way up. size_t size = path.size(); for (size_t i = 1; i < size; ++i) { const EventContext& eventContext = path.contextAt(i); if (eventContext.currentTargetSameAsTarget()) event.setEventPhase(Event::AT_TARGET); else if (event.bubbles() && !event.cancelBubble()) event.setEventPhase(Event::BUBBLING_PHASE); else continue; eventContext.handleLocalEvents(event); if (event.propagationStopped()) return; } if (event.bubbles() && !event.cancelBubble()) { event.setEventPhase(Event::BUBBLING_PHASE); if (shouldFireEventAtWindow) windowEventContext.handleLocalEvents(event); } }
void EventRetargeter::calculateAdjustedNodes(const Node* node, const Node* relatedNode, EventWithRelatedTargetDispatchBehavior eventWithRelatedTargetDispatchBehavior, EventPath& eventPath, AdjustedNodes& adjustedNodes) { RelatedNodeMap relatedNodeMap; buildRelatedNodeMap(relatedNode, relatedNodeMap); // Synthetic mouse events can have a relatedTarget which is identical to the target. bool targetIsIdenticalToToRelatedTarget = (node == relatedNode); TreeScope* lastTreeScope = 0; Node* adjustedNode = 0; for (EventPath::const_iterator iter = eventPath.begin(); iter < eventPath.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. adjustedNodes.append(adjustedNode); } else { adjustedNode = findRelatedNode(scope, relatedNodeMap); adjustedNodes.append(adjustedNode); } lastTreeScope = scope; if (eventWithRelatedTargetDispatchBehavior == DoesNotStopAtBoundary) continue; if (targetIsIdenticalToToRelatedTarget) { if (node->treeScope()->rootNode() == (*iter)->node()) { eventPath.shrink(iter + 1 - eventPath.begin()); break; } } else if ((*iter)->target() == adjustedNode) { // Event dispatching should be stopped here. eventPath.shrink(iter - eventPath.begin()); adjustedNodes.shrink(adjustedNodes.size() - 1); break; } } }
void EventRetargeter::adjustTouchList(const Node* node, const TouchList* touchList, const EventPath& eventPath, EventPathTouchLists& eventPathTouchLists) { if (!touchList) return; size_t eventPathSize = eventPath.size(); ASSERT(eventPathTouchLists.size() == eventPathSize); for (size_t i = 0; i < touchList->length(); ++i) { const Touch& touch = *touchList->item(i); AdjustedNodes adjustedNodes; calculateAdjustedNodes(node, touch.target()->toNode(), DoesNotStopAtBoundary, const_cast<EventPath&>(eventPath), adjustedNodes); ASSERT(adjustedNodes.size() == eventPathSize); for (size_t j = 0; j < eventPathSize; ++j) eventPathTouchLists[j]->append(touch.cloneWithNewTarget(adjustedNodes[j].get())); } }
static bool eventHasListeners(const AtomicString& eventType, DOMWindow* window, Node* node, const EventPath& eventPath) { if (window && window->hasEventListeners(eventType)) return true; if (node->hasEventListeners(eventType)) return true; for (size_t i = 0; i < eventPath.size(); i++) { if (eventPath[i]->node()->hasEventListeners(eventType)) return true; } return false; }
void EventRetargeter::adjustForRelatedTarget(const Node* node, EventTarget* relatedTarget, EventPath& eventPath) { if (!node) return; if (!relatedTarget) return; Node* relatedNode = relatedTarget->toNode(); if (!relatedNode) return; AdjustedNodes adjustedNodes; calculateAdjustedNodes(node, relatedNode, StopAtBoundaryIfNeeded, eventPath, adjustedNodes); ASSERT(adjustedNodes.size() <= eventPath.size()); for (size_t i = 0; i < adjustedNodes.size(); ++i) { ASSERT(eventPath[i]->isMouseOrFocusEventContext()); MouseOrFocusEventContext* mouseOrFocusEventContext = static_cast<MouseOrFocusEventContext*>(eventPath[i].get()); mouseOrFocusEventContext->setRelatedTarget(adjustedNodes[i]); } }
void EventRetargeter::adjustForTouchEvent(Node* node, const TouchEvent& touchEvent, EventPath& eventPath) { size_t eventPathSize = eventPath.size(); EventPathTouchLists eventPathTouches(eventPathSize); EventPathTouchLists eventPathTargetTouches(eventPathSize); EventPathTouchLists eventPathChangedTouches(eventPathSize); for (size_t i = 0; i < eventPathSize; ++i) { ASSERT(eventPath[i]->isTouchEventContext()); TouchEventContext* touchEventContext = toTouchEventContext(eventPath[i].get()); eventPathTouches[i] = touchEventContext->touches(); eventPathTargetTouches[i] = touchEventContext->targetTouches(); eventPathChangedTouches[i] = touchEventContext->changedTouches(); } adjustTouchList(node, touchEvent.touches(), eventPath, eventPathTouches); adjustTouchList(node, touchEvent.targetTouches(), eventPath, eventPathTargetTouches); adjustTouchList(node, touchEvent.changedTouches(), eventPath, eventPathChangedTouches); }