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(); |