Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: Source/core/page/EventHandler.cpp

Issue 1047733002: Fixed mouseenter/mouseleave event firing order. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/core/page/EventHandler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv ed. 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv ed.
3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) 3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) 4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 1428 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 1439
1440 // Treat any mouse move events as readonly if the user is currently touching the screen. 1440 // Treat any mouse move events as readonly if the user is currently touching the screen.
1441 if (m_touchPressed) 1441 if (m_touchPressed)
1442 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; 1442 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1443 HitTestRequest request(hitType); 1443 HitTestRequest request(hitType);
1444 MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(mouseEvent, HitTestResult(LayoutPoint())); 1444 MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(mouseEvent, HitTestResult(LayoutPoint()));
1445 1445
1446 // We don't want to do a hit-test in forceLeave scenarios because there migh t actually be some other frame above this one at the specified co-ordinate. 1446 // We don't want to do a hit-test in forceLeave scenarios because there migh t actually be some other frame above this one at the specified co-ordinate.
1447 // So we must force the hit-test to fail, while still clearing hover/active state. 1447 // So we must force the hit-test to fail, while still clearing hover/active state.
1448 if (forceLeave) 1448 if (forceLeave)
1449 m_frame->document()->updateHoverActiveState(request, 0, &mouseEvent); 1449 m_frame->document()->updateHoverActiveState(request, 0);
1450 else 1450 else
1451 mev = prepareMouseEvent(request, mouseEvent); 1451 mev = prepareMouseEvent(request, mouseEvent);
1452 1452
1453 if (hoveredNode) 1453 if (hoveredNode)
1454 *hoveredNode = mev.hitTestResult(); 1454 *hoveredNode = mev.hitTestResult();
1455 1455
1456 Scrollbar* scrollbar = nullptr; 1456 Scrollbar* scrollbar = nullptr;
1457 1457
1458 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) 1458 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1459 m_resizeScrollableArea->resize(mouseEvent, m_offsetFromResizeCorner); 1459 m_resizeScrollableArea->resize(mouseEvent, m_offsetFromResizeCorner);
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
1550 return true; 1550 return true;
1551 } 1551 }
1552 1552
1553 if (m_frameSetBeingResized) 1553 if (m_frameSetBeingResized)
1554 return !dispatchMouseEvent(EventTypeNames::mouseup, m_frameSetBeingResiz ed.get(), m_clickCount, mouseEvent, false); 1554 return !dispatchMouseEvent(EventTypeNames::mouseup, m_frameSetBeingResiz ed.get(), m_clickCount, mouseEvent, false);
1555 1555
1556 if (m_lastScrollbarUnderMouse) { 1556 if (m_lastScrollbarUnderMouse) {
1557 invalidateClick(); 1557 invalidateClick();
1558 m_lastScrollbarUnderMouse->mouseUp(mouseEvent); 1558 m_lastScrollbarUnderMouse->mouseUp(mouseEvent);
1559 bool setUnder = false; 1559 bool setUnder = false;
1560 return !dispatchMouseEvent(EventTypeNames::mouseup, m_lastNodeUnderMouse .get(), m_clickCount, mouseEvent, setUnder); 1560 return !dispatchMouseEvent(EventTypeNames::mouseup, m_nodeUnderMouse.get (), m_clickCount, mouseEvent, setUnder);
1561 } 1561 }
1562 1562
1563 // Mouse events simulated from touch should not hit-test again. 1563 // Mouse events simulated from touch should not hit-test again.
1564 ASSERT(!mouseEvent.fromTouch()); 1564 ASSERT(!mouseEvent.fromTouch());
1565 1565
1566 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Release; 1566 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Release;
1567 HitTestRequest request(hitType); 1567 HitTestRequest request(hitType);
1568 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); 1568 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1569 LocalFrame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetN ode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); 1569 LocalFrame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetN ode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1570 if (m_eventHandlerWillResetCapturingMouseEventsNode) 1570 if (m_eventHandlerWillResetCapturingMouseEventsNode)
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
1826 } 1826 }
1827 1827
1828 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestReques t& request, const PlatformMouseEvent& mev) 1828 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestReques t& request, const PlatformMouseEvent& mev)
1829 { 1829 {
1830 ASSERT(m_frame); 1830 ASSERT(m_frame);
1831 ASSERT(m_frame->document()); 1831 ASSERT(m_frame->document());
1832 1832
1833 return m_frame->document()->prepareMouseEvent(request, contentPointFromRootF rame(m_frame, mev.position()), mev); 1833 return m_frame->document()->prepareMouseEvent(request, contentPointFromRootF rame(m_frame, mev.position()), mev);
1834 } 1834 }
1835 1835
1836 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo useEvent& mouseEvent, bool fireMouseOverOut) 1836 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo useEvent& mouseEvent, bool fireMouseEvents)
1837 { 1837 {
1838 Node* result = targetNode; 1838 Node* result = targetNode;
1839 1839
1840 // If we're capturing, we always go right to that node. 1840 // If we're capturing, we always go right to that node.
1841 if (m_capturingMouseEventsNode) 1841 if (m_capturingMouseEventsNode)
1842 result = m_capturingMouseEventsNode.get(); 1842 result = m_capturingMouseEventsNode.get();
1843 else { 1843 else {
1844 // If the target node is a text node, dispatch on the parent node - rdar ://4196646 1844 // If the target node is a text node, dispatch on the parent node - rdar ://4196646
1845 if (result && result->isTextNode()) 1845 if (result && result->isTextNode())
1846 result = NodeRenderingTraversal::parent(*result); 1846 result = NodeRenderingTraversal::parent(*result);
1847 } 1847 }
1848 m_nodeUnderMouse = result; 1848 m_nodeUnderMouse = result;
1849 1849
1850 // Fire mouseout/mouseover if the mouse has shifted to a different node. 1850 if (fireMouseEvents) {
1851 if (fireMouseOverOut) {
1852 DeprecatedPaintLayer* layerForLastNode = layerForNode(m_lastNodeUnderMou se.get()); 1851 DeprecatedPaintLayer* layerForLastNode = layerForNode(m_lastNodeUnderMou se.get());
1853 DeprecatedPaintLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderM ouse.get()); 1852 DeprecatedPaintLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderM ouse.get());
1854 Page* page = m_frame->page(); 1853 Page* page = m_frame->page();
1855 1854
1856 if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->docu ment() != m_frame->document())) { 1855 if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->docu ment() != m_frame->document())) {
1857 // The mouse has moved between frames. 1856 // The mouse has moved between frames.
1858 if (LocalFrame* frame = m_lastNodeUnderMouse->document().frame()) { 1857 if (LocalFrame* frame = m_lastNodeUnderMouse->document().frame()) {
1859 if (FrameView* frameView = frame->view()) 1858 if (FrameView* frameView = frame->view())
1860 frameView->mouseExitedContentArea(); 1859 frameView->mouseExitedContentArea();
1861 } 1860 }
(...skipping 13 matching lines...) Expand all
1875 // The mouse has moved between layers. 1874 // The mouse has moved between layers.
1876 if (ScrollableArea* scrollableAreaForNodeUnderMouse = associatedScro llableArea(layerForNodeUnderMouse)) 1875 if (ScrollableArea* scrollableAreaForNodeUnderMouse = associatedScro llableArea(layerForNodeUnderMouse))
1877 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea(); 1876 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
1878 } 1877 }
1879 1878
1880 if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame- >document()) { 1879 if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame- >document()) {
1881 m_lastNodeUnderMouse = nullptr; 1880 m_lastNodeUnderMouse = nullptr;
1882 m_lastScrollbarUnderMouse = nullptr; 1881 m_lastScrollbarUnderMouse = nullptr;
1883 } 1882 }
1884 1883
1885 if (m_lastNodeUnderMouse != m_nodeUnderMouse) { 1884 if (m_lastNodeUnderMouse != m_nodeUnderMouse)
1886 // send mouseout event to the old node 1885 sendMouseEventsForNodeTransition(m_lastNodeUnderMouse.get(), m_nodeU nderMouse.get(), mouseEvent);
1887 if (m_lastNodeUnderMouse) 1886
1888 m_lastNodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNa mes::mouseout, 0, m_nodeUnderMouse.get());
1889 // send mouseover event to the new node
1890 if (m_nodeUnderMouse)
1891 m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNames: :mouseover, 0, m_lastNodeUnderMouse.get());
1892 }
1893 m_lastNodeUnderMouse = m_nodeUnderMouse; 1887 m_lastNodeUnderMouse = m_nodeUnderMouse;
1894 } 1888 }
1895 } 1889 }
1896 1890
1891 void EventHandler::sendMouseEventsForNodeTransition(Node* exitedNode, Node* ente redNode, const PlatformMouseEvent& mouseEvent)
1892 {
1893 ASSERT(exitedNode != enteredNode);
1894
1895 // First, dispatch mouseout and mouseover events (which bubble to ancestors)
1896 if (exitedNode)
1897 exitedNode->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseout, 0, enteredNode);
1898 if (enteredNode)
1899 enteredNode->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseover, 0 , exitedNode);
1900
1901 // Then dispatch mouseenter and mouseleave events. These are non-bubbling ev ents, and they are dispatched if there
1902 // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special
1903 // handling is necessary to avoid O(n^2) capturing event handler checks.
1904 //
1905 // Note, however, that this optimization can possibly cause some unanswere d/missing/redundant mouseenter or
1906 // mouseleave events in certain contrived eventhandling scenarios, e.g., whe n:
1907 // - the mouseleave handler for a node sets the only capturing-mouseleave-li stener in its ancestor, or
1908 // - DOM mods in any mouseenter/mouseleave handler changes the common ancest or of exited & entered nodes, etc.
1909 // We think the spec specifies a "frozen" state to avoid such corner cases ( check the discussion on "candidate event
1910 // listeners" at http://www.w3.org/TR/uievents), but our code below preserve s one such behavior from past only to
1911 // match Firefox and IE behavior.
1912 //
1913 // TODO(mustaq): Confirm spec conformance, double-check with other browsers.
1914
1915 // Create lists of all exited/entered ancestors.
1916 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> exitedAncestors;
1917 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> enteredAncestors;
1918 for (Node* node = exitedNode; node; node = NodeRenderingTraversal::parent(*n ode)) {
1919 exitedAncestors.append(node);
1920 }
1921 for (Node* node = enteredNode; node; node = NodeRenderingTraversal::parent(* node)) {
1922 enteredAncestors.append(node);
1923 }
1924
1925 size_t numExitedAncestors = exitedAncestors.size();
1926 size_t numEnteredAncestors = enteredAncestors.size();
1927
1928 // Locate the common ancestor in the two lists. Start with the assumption th at it's off both the lists.
1929 size_t exitedAncestorIndex = numExitedAncestors;
1930 size_t enteredAncestorIndex = numEnteredAncestors;
1931 for (size_t j = 0; j < numExitedAncestors; j++) {
1932 for (size_t i = 0; i < numEnteredAncestors; i++) {
1933 if (exitedAncestors[j] == enteredAncestors[i]) {
1934 exitedAncestorIndex = j;
1935 enteredAncestorIndex = i;
1936 break;
1937 }
1938 }
1939 if (exitedAncestorIndex < numExitedAncestors)
1940 break;
1941 }
1942
1943 // Determine if there is a capturing mouseleave listener in an ancestor.
1944 bool exitedNodeHasCapturingAncestor = false;
1945 for (size_t j = 0; j < numExitedAncestors; j++) {
1946 if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouse leave))
1947 exitedNodeHasCapturingAncestor = true;
1948 }
1949
1950 // Send mouseleave events to appropriate exited ancestors, in child-to-paren t order.
1951 for (size_t j = 0; j < exitedAncestorIndex; j++) {
1952 if (exitedNodeHasCapturingAncestor || exitedAncestors[j]->hasEventListen ers(EventTypeNames::mouseleave))
1953 exitedAncestors[j]->dispatchMouseEvent(mouseEvent, EventTypeNames::m ouseleave, 0, enteredNode);
1954 }
1955
1956 // Determine if there is a capturing mouseenter listener in an ancestor. Thi s must be done /after/ dispatching the
1957 // mouseleave events because the handler for mouseleave might set a capturin g mouseenter handler.
1958 bool enteredNodeHasCapturingAncestor = false;
1959 for (size_t i = 0; i < numEnteredAncestors; i++) {
1960 if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mous eenter))
1961 enteredNodeHasCapturingAncestor = true;
1962 }
1963
1964 // Send mouseenter events to appropriate entered ancestors, in parent-to-chi ld order.
1965 for (size_t i = enteredAncestorIndex; i > 0; i--) {
1966 if (enteredNodeHasCapturingAncestor || enteredAncestors[i-1]->hasEventLi steners(EventTypeNames::mouseenter))
1967 enteredAncestors[i-1]->dispatchMouseEvent(mouseEvent, EventTypeNames ::mouseenter, 0, exitedNode);
1968 }
1969 }
1970
1897 // The return value means 'continue default handling.' 1971 // The return value means 'continue default handling.'
1972 // TODO(mustaq): setUnder needs a more informative name.
1898 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe tNode, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder) 1973 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe tNode, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
1899 { 1974 {
1900 updateMouseEventTargetNode(targetNode, mouseEvent, setUnder); 1975 updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
1901 return !m_nodeUnderMouse || m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount); 1976 return !m_nodeUnderMouse || m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount);
1902 } 1977 }
1903 1978
1904 // The return value means 'swallow event' (was handled), as for other handle* fu nctions. 1979 // The return value means 'swallow event' (was handled), as for other handle* fu nctions.
1905 bool EventHandler::handleMouseFocus(const MouseEventWithHitTestResults& targeted Event) 1980 bool EventHandler::handleMouseFocus(const MouseEventWithHitTestResults& targeted Event)
1906 { 1981 {
1907 const PlatformMouseEvent& mouseEvent = targetedEvent.event(); 1982 const PlatformMouseEvent& mouseEvent = targetedEvent.event();
(...skipping 2041 matching lines...) Expand 10 before | Expand all | Expand 10 after
3949 4024
3950 // If it's in the direction to hide the top controls, only consume when the frame can also scroll. 4025 // If it's in the direction to hide the top controls, only consume when the frame can also scroll.
3951 if (m_frame->view()->scrollPosition().y() < m_frame->view()->maximumScrollPo sition().y()) 4026 if (m_frame->view()->scrollPosition().y() < m_frame->view()->maximumScrollPo sition().y())
3952 return true; 4027 return true;
3953 4028
3954 return false; 4029 return false;
3955 } 4030 }
3956 4031
3957 4032
3958 } // namespace blink 4033 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/page/EventHandler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698