Chromium Code Reviews| Index: Source/core/dom/Document.cpp |
| diff --git a/Source/core/dom/Document.cpp b/Source/core/dom/Document.cpp |
| index 30687207b4c4beb57bae8b01403e1cd1678204e4..6b6900f01ed739fa3930116e1ccf7daa40b4ca44 100644 |
| --- a/Source/core/dom/Document.cpp |
| +++ b/Source/core/dom/Document.cpp |
| @@ -2895,7 +2895,7 @@ MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& r |
| renderView()->hitTest(request, result); |
| if (!request.readOnly()) |
| - updateHoverActiveState(request, result.innerElement()); |
| + updateHoverActiveState(request, result.innerElement(), &event); |
| return MouseEventWithHitTestResults(event, result); |
| } |
| @@ -4990,13 +4990,13 @@ static RenderObject* nearestCommonHoverAncestor(RenderObject* obj1, RenderObject |
| return 0; |
| } |
| -void Document::updateHoverActiveState(const HitTestRequest& request, Element* innerElement) |
| +void Document::updateHoverActiveState(const HitTestRequest& request, Element* innerElement, const PlatformMouseEvent* event) |
| { |
| ASSERT(!request.readOnly()); |
| Element* innerElementInDocument = innerElement; |
| while (innerElementInDocument && innerElementInDocument->document() != this) { |
| - innerElementInDocument->document()->updateHoverActiveState(request, innerElementInDocument); |
| + innerElementInDocument->document()->updateHoverActiveState(request, innerElementInDocument, event); |
| innerElementInDocument = innerElementInDocument->document()->ownerElement(); |
| } |
| @@ -5059,6 +5059,28 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in |
| Vector<RefPtr<Node>, 32> nodesToRemoveFromChain; |
| Vector<RefPtr<Node>, 32> nodesToAddToChain; |
| + // mouseenter and mouseleave events do not bubble, so they are dispatched iff there is a capturing |
|
PhistucK
2013/07/08 17:57:26
s/iff/if
Mike West
2013/07/09 09:37:00
"iff" is short for "if and only if" (e.g. "necessa
|
| + // event handler on an ancestor or a normal event handler on the element itself. This special |
| + // handling is necessary to avoid O(n^2) capturing event handler checks. |
| + bool hasCapturingMouseEnterListener = false; |
| + bool hasCapturingMouseLeaveListener = false; |
| + if (event && newHoverNode != oldHoverNode.get()) { |
| + // Walk up the newly hovered node's ancestor tree for 'mouseenter' events, and the previously |
| + // hovered node's ancestor tree for 'mouseleave' events. |
| + for (Node* node = newHoverNode; node; node = node->parentOrShadowHostNode()) { |
| + if (node->hasCapturingEventListeners(eventNames().mouseenterEvent)) { |
| + hasCapturingMouseEnterListener = true; |
| + break; |
| + } |
| + } |
| + for (Node* node = oldHoverNode.get(); node; node = node->parentOrShadowHostNode()) { |
| + if (node->hasCapturingEventListeners(eventNames().mouseleaveEvent)) { |
| + hasCapturingMouseLeaveListener = true; |
| + break; |
| + } |
| + } |
| + } |
| + |
| if (oldHoverObj != newHoverObj) { |
| // If the old hovered node is not nil but it's renderer is, it was probably detached as part of the :hover style |
| // (for instance by setting display:none in the :hover pseudo-class). In this case, the old hovered element (and its ancestors) |
| @@ -5085,8 +5107,11 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in |
| } |
| size_t removeCount = nodesToRemoveFromChain.size(); |
| - for (size_t i = 0; i < removeCount; ++i) |
| + for (size_t i = 0; i < removeCount; ++i) { |
| nodesToRemoveFromChain[i]->setHovered(false); |
| + if (event && (hasCapturingMouseLeaveListener || nodesToRemoveFromChain[i]->hasEventListeners(eventNames().mouseleaveEvent))) |
| + nodesToRemoveFromChain[i]->dispatchMouseEvent(*event, eventNames().mouseleaveEvent, 0, newHoverNode); |
| + } |
| bool sawCommonAncestor = false; |
| size_t addCount = nodesToAddToChain.size(); |
| @@ -5096,8 +5121,11 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in |
| sawCommonAncestor = true; |
| if (allowActiveChanges) |
| nodesToAddToChain[i]->setActive(true); |
| - if (!sawCommonAncestor) |
| + if (!sawCommonAncestor) { |
| nodesToAddToChain[i]->setHovered(true); |
| + if (event && (hasCapturingMouseEnterListener || nodesToAddToChain[i]->hasEventListeners(eventNames().mouseenterEvent))) |
|
esprehn
2013/07/08 18:13:02
This is not correct, I could add a listener inside
Mike West
2013/07/09 09:37:00
You're right; I didn't think of that case at all.
|
| + nodesToAddToChain[i]->dispatchMouseEvent(*event, eventNames().mouseenterEvent, 0, oldHoverNode.get()); |
| + } |
| } |
| updateStyleIfNeeded(); |