Mouseenter and mouseleave events not supported
authorallan.jensen@digia.com <allan.jensen@digia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Apr 2013 09:42:11 +0000 (09:42 +0000)
committerallan.jensen@digia.com <allan.jensen@digia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 Apr 2013 09:42:11 +0000 (09:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=18930

Reviewed by David Hyatt.

Source/WebCore:

Implements mouseenter and mouseleave events from W3C DOM Level 3 Events.
These event are already supported by all other major browsers.

To avoid performance regressions the new events are only dispatched when
there are event listeners for them.

Tests: fast/events/mouseenter-mouseleave-capture.html
       fast/events/mouseenter-mouseleave.html

* bindings/scripts/CodeGenerator.pm:
* dom/Document.cpp:
(WebCore::Document::prepareMouseEvent):
(WebCore::Document::updateHoverActiveState):
* dom/Document.h:
(Document):
* dom/Document.idl:
* dom/Element.h:
(Element):
* dom/Element.idl:
* dom/EventListenerMap.cpp:
(WebCore::EventListenerMap::containsCapturing):
* dom/EventListenerMap.h:
(EventListenerMap):
* dom/EventNames.h:
* dom/EventTarget.h:
(EventTarget):
(WebCore::EventTarget::hasCapturingEventListeners):
* dom/MouseEvent.cpp:
(WebCore::MouseEvent::create):
(WebCore::MouseEvent::toElement):
(WebCore::MouseEvent::fromElement):
* html/HTMLAttributeNames.in:
* html/HTMLElement.cpp:
(WebCore::HTMLElement::eventNameForAttributeName):
* page/DOMWindow.h:
(DOMWindow):
* page/DOMWindow.idl:
* svg/SVGElement.cpp:
(WebCore::SVGElement::parseAttribute):
* svg/SVGElementInstance.h:
(SVGElementInstance):
* svg/SVGElementInstance.idl:

LayoutTests:

To new tests that mouseenter and mouseleave works in both bubbling and capture phase.

* fast/events/mouseenter-mouseleave-capture-expected.txt: Added.
* fast/events/mouseenter-mouseleave-capture.html: Added.
* fast/events/mouseenter-mouseleave-expected.txt: Added.
* fast/events/mouseenter-mouseleave.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@149173 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/mouseenter-mouseleave-capture-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/mouseenter-mouseleave-capture.html [new file with mode: 0644]
LayoutTests/fast/events/mouseenter-mouseleave-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/mouseenter-mouseleave.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/bindings/scripts/CodeGenerator.pm
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
Source/WebCore/dom/Element.h
Source/WebCore/dom/Element.idl
Source/WebCore/dom/EventListenerMap.cpp
Source/WebCore/dom/EventListenerMap.h
Source/WebCore/dom/EventNames.h
Source/WebCore/dom/EventTarget.h
Source/WebCore/dom/MouseEvent.cpp
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLElement.cpp
Source/WebCore/page/DOMWindow.h
Source/WebCore/page/DOMWindow.idl
Source/WebCore/svg/SVGElement.cpp
Source/WebCore/svg/SVGElementInstance.h
Source/WebCore/svg/SVGElementInstance.idl

index 994d0a2..577cc56 100644 (file)
@@ -1,3 +1,17 @@
+2013-04-26  Allan Sandfeld Jensen  <allan.jensen@digia.com>
+
+        Mouseenter and mouseleave events not supported
+        https://bugs.webkit.org/show_bug.cgi?id=18930
+
+        Reviewed by David Hyatt.
+
+        To new tests that mouseenter and mouseleave works in both bubbling and capture phase.
+
+        * fast/events/mouseenter-mouseleave-capture-expected.txt: Added.
+        * fast/events/mouseenter-mouseleave-capture.html: Added.
+        * fast/events/mouseenter-mouseleave-expected.txt: Added.
+        * fast/events/mouseenter-mouseleave.html: Added.
+
 2013-04-26  Christophe Dumez  <ch.dumez@sisa.samsung.com>
 
         [Qt] REGRESSION(r149001): It made two fast/dom/DeviceMotion tests fail.
diff --git a/LayoutTests/fast/events/mouseenter-mouseleave-capture-expected.txt b/LayoutTests/fast/events/mouseenter-mouseleave-capture-expected.txt
new file mode 100644 (file)
index 0000000..6e19f5a
--- /dev/null
@@ -0,0 +1,9 @@
+mouseenter on outer1
+mouseenter on inner1
+mouseleave on inner1
+mouseenter on inner3
+mouseenter on inner2
+mouseleave on inner3
+mouseleave on inner2
+mouseleave on outer1
+
diff --git a/LayoutTests/fast/events/mouseenter-mouseleave-capture.html b/LayoutTests/fast/events/mouseenter-mouseleave-capture.html
new file mode 100644 (file)
index 0000000..8fb587a
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function log(message) {
+    document.getElementById('console').innerHTML += (message + "\n");
+}
+
+function logMouseEvent(ev) {
+    var target = (ev.target)? ev.target : ev.srcElement;
+    if (target.id) {
+        log(ev.type + " on " + target.id);
+    }
+}
+
+function doTest() {
+    document.body.offsetLeft;
+    document.body.addEventListener('mouseenter',logMouseEvent,true);
+    document.body.addEventListener('mouseleave',logMouseEvent,true);
+    if (window.testRunner) {
+        eventSender.mouseMoveTo(1, 1);
+        eventSender.mouseMoveTo(90,140);
+        eventSender.mouseMoveTo(110,140);
+        eventSender.mouseMoveTo(130,140);
+        eventSender.mouseMoveTo(170,140);
+        eventSender.mouseMoveTo(180,140);
+        eventSender.mouseMoveTo(210,140);
+
+        testRunner.dumpAsText();
+    }
+}
+</script>
+</head>
+<body onload="doTest()">
+<div id="outer1" style="width:100px; height:100px; background-color:blue; top:100px; left:100px; position:absolute">
+<div id="inner1" style="width:50px; height:50px; background-color:red; top:20px; left:20px; position:absolute">
+</div>
+<div id="inner2" style="width:20px; height:20px; background-color:yellow; top:30px; left:60px; position:absolute">
+<div id="inner3" style="margin:5px; width:10px; height:10px; background-color:green;">
+</div>
+</div>
+</div>
+<pre id="console"></pre>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/mouseenter-mouseleave-expected.txt b/LayoutTests/fast/events/mouseenter-mouseleave-expected.txt
new file mode 100644 (file)
index 0000000..432a349
--- /dev/null
@@ -0,0 +1,24 @@
+mouseover on outer1
+mouseout on outer1
+mouseover on inner1
+mouseover on inner1
+mouseout on inner1
+mouseout on inner1
+mouseover on inner3
+mouseover on inner3
+mouseover on inner3
+mouseout on inner3
+mouseout on inner3
+mouseout on inner3
+mouseover on outer1
+mouseout on outer1
+
+mouseenter on outer1
+mouseenter on inner1
+mouseleave on inner1
+mouseenter on inner3
+mouseenter on inner2
+mouseleave on inner3
+mouseleave on inner2
+mouseleave on outer1
+
diff --git a/LayoutTests/fast/events/mouseenter-mouseleave.html b/LayoutTests/fast/events/mouseenter-mouseleave.html
new file mode 100644 (file)
index 0000000..5982b70
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function log(message, console) {
+    document.getElementById(console).innerHTML += (message + "\n");
+}
+
+function logMouseOverOutEvent(ev) {
+    var target = (ev.target)? ev.target : ev.srcElement;
+    log(ev.type + " on " + target.id, 'console1');
+}
+
+function logMouseEnterLeaveEvent(ev) {
+    var target = (ev.target)? ev.target : ev.srcElement;
+    log(ev.type + " on " + target.id, 'console2');
+}
+
+function doTest() {
+    document.body.offsetLeft;
+    if (window.testRunner) {
+        eventSender.mouseMoveTo(1, 1);
+        eventSender.mouseMoveTo(90,140);
+        eventSender.mouseMoveTo(110,140);
+        eventSender.mouseMoveTo(130,140);
+        eventSender.mouseMoveTo(170,140);
+        eventSender.mouseMoveTo(180,140);
+        eventSender.mouseMoveTo(210,140);
+
+        testRunner.dumpAsText();
+    }
+}
+</script>
+</head>
+<body onload="doTest()">
+<div id="outer1" style="width:100px; height:100px; background-color:blue; top:100px; left:100px; position:absolute"
+     onMouseOver="logMouseOverOutEvent(event)" onMouseOut="logMouseOverOutEvent(event)"
+     onMouseEnter="logMouseEnterLeaveEvent(event)" onMouseLeave="logMouseEnterLeaveEvent(event)">
+<div id="inner1" style="width:50px; height:50px; background-color:red; top:20px; left:20px; position:absolute"
+     onMouseOver="logMouseOverOutEvent(event)" onMouseOut="logMouseOverOutEvent(event)"
+     onMouseEnter="logMouseEnterLeaveEvent(event)" onMouseLeave="logMouseEnterLeaveEvent(event)">
+</div>
+<div id="inner2" style="width:20px; height:20px; background-color:yellow; top:30px; left:60px; position:absolute"
+     onMouseOver="logMouseOverOutEvent(event)" onMouseOut="logMouseOverOutEvent(event)"
+     onMouseEnter="logMouseEnterLeaveEvent(event)" onMouseLeave="logMouseEnterLeaveEvent(event)">
+<div id="inner3" style="margin:5px; width:10px; height:10px; background-color:green;"
+     onMouseOver="logMouseOverOutEvent(event)" onMouseOut="logMouseOverOutEvent(event)"
+     onMouseEnter="logMouseEnterLeaveEvent(event)" onMouseLeave="logMouseEnterLeaveEvent(event)">
+</div>
+</div>
+</div>
+<pre id="console1"></pre>
+<br>
+<pre id="console2"></pre>
+</body>
+</html>
index cce0a58..4299304 100644 (file)
@@ -1,3 +1,53 @@
+2013-04-26  Allan Sandfeld Jensen  <allan.jensen@digia.com>
+
+        Mouseenter and mouseleave events not supported
+        https://bugs.webkit.org/show_bug.cgi?id=18930
+
+        Reviewed by David Hyatt.
+
+        Implements mouseenter and mouseleave events from W3C DOM Level 3 Events.
+        These event are already supported by all other major browsers.
+
+        To avoid performance regressions the new events are only dispatched when
+        there are event listeners for them.
+
+        Tests: fast/events/mouseenter-mouseleave-capture.html
+               fast/events/mouseenter-mouseleave.html
+
+        * bindings/scripts/CodeGenerator.pm:
+        * dom/Document.cpp:
+        (WebCore::Document::prepareMouseEvent):
+        (WebCore::Document::updateHoverActiveState):
+        * dom/Document.h:
+        (Document):
+        * dom/Document.idl:
+        * dom/Element.h:
+        (Element):
+        * dom/Element.idl:
+        * dom/EventListenerMap.cpp:
+        (WebCore::EventListenerMap::containsCapturing):
+        * dom/EventListenerMap.h:
+        (EventListenerMap):
+        * dom/EventNames.h:
+        * dom/EventTarget.h:
+        (EventTarget):
+        (WebCore::EventTarget::hasCapturingEventListeners):
+        * dom/MouseEvent.cpp:
+        (WebCore::MouseEvent::create):
+        (WebCore::MouseEvent::toElement):
+        (WebCore::MouseEvent::fromElement):
+        * html/HTMLAttributeNames.in:
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::eventNameForAttributeName):
+        * page/DOMWindow.h:
+        (DOMWindow):
+        * page/DOMWindow.idl:
+        * svg/SVGElement.cpp:
+        (WebCore::SVGElement::parseAttribute):
+        * svg/SVGElementInstance.h:
+        (SVGElementInstance):
+        * svg/SVGElementInstance.idl:
+
 2013-04-26  Christophe Dumez  <ch.dumez@sisa.samsung.com>
 
         Add support for Web IDL partial interfaces to the bindings generator
index 6a7ccb9..292df64 100644 (file)
@@ -67,6 +67,7 @@ my %svgAnimatedTypeHash = ("SVGAnimatedAngle" => 1, "SVGAnimatedBoolean" => 1,
 
 my %svgAttributesInHTMLHash = ("class" => 1, "id" => 1, "onabort" => 1, "onclick" => 1,
                                "onerror" => 1, "onload" => 1, "onmousedown" => 1,
+                               "onmouseenter" => 1, "onmouseleave" => 1,
                                "onmousemove" => 1, "onmouseout" => 1, "onmouseover" => 1,
                                "onmouseup" => 1, "onresize" => 1, "onscroll" => 1,
                                "onunload" => 1);
index ffcbf83..97aea8b 100644 (file)
@@ -3051,7 +3051,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);
 }
@@ -5901,7 +5901,7 @@ 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());
 
@@ -5970,6 +5970,26 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in
     Vector<RefPtr<Node>, 32> nodesToRemoveFromChain;
     Vector<RefPtr<Node>, 32> nodesToAddToChain;
 
+    // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor
+    // or a normal eventhandler on the element itself (they don't bubble).
+    // This optimization is necessary since these events can cause O(n²) capturing event-handler checks.
+    bool hasCapturingMouseEnterListener = false;
+    bool hasCapturingMouseLeaveListener = false;
+    if (event && newHoverNode != oldHoverNode.get()) {
+        for (Node* curr = newHoverNode; curr; curr = curr->parentOrShadowHostNode()) {
+            if (curr->hasCapturingEventListeners(eventNames().mouseenterEvent)) {
+                hasCapturingMouseEnterListener = true;
+                break;
+            }
+        }
+        for (Node* curr = oldHoverNode.get(); curr; curr = curr->parentOrShadowHostNode()) {
+            if (curr->hasCapturingEventListeners(eventNames().mouseleaveEvent)) {
+                hasCapturingMouseLeaveListener = true;
+                break;
+            }
+        }
+    }
+
     if (oldHoverObj != newHoverObj) {
         // The old hover path only needs to be cleared up to (and not including) the common ancestor;
         for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) {
@@ -5990,14 +6010,25 @@ 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();
     for (size_t i = 0; i < addCount; ++i) {
         if (allowActiveChanges)
             nodesToAddToChain[i]->setActive(true);
-        nodesToAddToChain[i]->setHovered(true);
+        if (ancestor && nodesToAddToChain[i] == ancestor->node())
+            sawCommonAncestor = true;
+        if (!sawCommonAncestor) {
+            // Elements after the common hover ancestor does not change hover state, but are iterated over because they may change active state.
+            nodesToAddToChain[i]->setHovered(true);
+            if (event && (hasCapturingMouseEnterListener || nodesToAddToChain[i]->hasEventListeners(eventNames().mouseenterEvent)))
+                nodesToAddToChain[i]->dispatchMouseEvent(*event, eventNames().mouseenterEvent, 0, oldHoverNode.get());
+        }
     }
 
     updateStyleIfNeeded();
index cf08414..fe8e325 100644 (file)
@@ -265,6 +265,8 @@ public:
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover);
@@ -698,7 +700,7 @@ public:
     void hoveredNodeDetached(Node*);
     void activeChainNodeDetached(Node*);
 
-    void updateHoverActiveState(const HitTestRequest&, Element*);
+    void updateHoverActiveState(const HitTestRequest&, Element*, const PlatformMouseEvent* = 0);
 
     // Updates for :target (CSS3 selector).
     void setCSSTarget(Element*);
index b8aea5a..cb682ef 100644 (file)
     [NotEnumerable] attribute EventListener onkeyup;
     [NotEnumerable] attribute EventListener onload;
     [NotEnumerable] attribute EventListener onmousedown;
+    [NotEnumerable] attribute EventListener onmouseenter;
+    [NotEnumerable] attribute EventListener onmouseleave;
     [NotEnumerable] attribute EventListener onmousemove;
     [NotEnumerable] attribute EventListener onmouseout;
     [NotEnumerable] attribute EventListener onmouseover;
index feb1b2d..4320954 100644 (file)
@@ -199,6 +199,8 @@ public:
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover);
index 0286679..2cd63d5 100644 (file)
     [NotEnumerable] attribute EventListener onkeyup;
     [NotEnumerable] attribute EventListener onload;
     [NotEnumerable] attribute EventListener onmousedown;
+    [NotEnumerable] attribute EventListener onmouseenter;
+    [NotEnumerable] attribute EventListener onmouseleave;
     [NotEnumerable] attribute EventListener onmousemove;
     [NotEnumerable] attribute EventListener onmouseout;
     [NotEnumerable] attribute EventListener onmouseover;
index 2b2dfea..a7df1aa 100644 (file)
@@ -78,6 +78,20 @@ bool EventListenerMap::contains(const AtomicString& eventType) const
     return false;
 }
 
+bool EventListenerMap::containsCapturing(const AtomicString& eventType) const
+{
+    for (unsigned i = 0; i < m_entries.size(); ++i) {
+        if (m_entries[i].first == eventType) {
+            const EventListenerVector* vector = m_entries[i].second.get();
+            for (unsigned j = 0; j < vector->size(); ++j) {
+                if (vector->at(j).useCapture)
+                    return true;
+            }
+        }
+    }
+    return false;
+}
+
 void EventListenerMap::clear()
 {
     assertNoActiveIterators();
index 6a036d3..e29b4c6 100644 (file)
@@ -50,6 +50,7 @@ public:
 
     bool isEmpty() const { return m_entries.isEmpty(); }
     bool contains(const AtomicString& eventType) const;
+    bool containsCapturing(const AtomicString& eventType) const;
 
     void clear();
     bool add(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
index 925844b..9627adb 100644 (file)
@@ -89,6 +89,8 @@ namespace WebCore {
     macro(loadstart) \
     macro(message) \
     macro(mousedown) \
+    macro(mouseenter) \
+    macro(mouseleave) \
     macro(mousemove) \
     macro(mouseout) \
     macro(mouseover) \
index ea0799b..869568a 100644 (file)
@@ -126,6 +126,7 @@ namespace WebCore {
 
         bool hasEventListeners();
         bool hasEventListeners(const AtomicString& eventType);
+        bool hasCapturingEventListeners(const AtomicString& eventType);
         const EventListenerVector& getEventListeners(const AtomicString& eventType);
 
         bool fireEventListeners(Event*);
@@ -206,6 +207,14 @@ namespace WebCore {
         return d->eventListenerMap.contains(eventType);
     }
 
+    inline bool EventTarget::hasCapturingEventListeners(const AtomicString& eventType)
+    {
+        EventTargetData* d = eventTargetData();
+        if (!d)
+            return false;
+        return d->eventListenerMap.containsCapturing(eventType);
+    }
+
 } // namespace WebCore
 
 #endif // EventTarget_h
index faa91a1..2e329a4 100644 (file)
@@ -57,9 +57,11 @@ PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRef
 {
     ASSERT(event.type() == PlatformEvent::MouseMoved || event.button() != NoButton);
 
-    bool isCancelable = eventType != eventNames().mousemoveEvent;
+    bool isMouseEnterOrLeave = eventType == eventNames().mouseenterEvent || eventType == eventNames().mouseleaveEvent;
+    bool isCancelable = eventType != eventNames().mousemoveEvent && !isMouseEnterOrLeave;
+    bool canBubble = !isMouseEnterOrLeave;
 
-    return MouseEvent::create(eventType, true, isCancelable, view,
+    return MouseEvent::create(eventType, canBubble, isCancelable, view,
         detail, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
 #if ENABLE(POINTER_LOCK)
         event.movementDelta().x(), event.movementDelta().y(),
@@ -202,7 +204,7 @@ int MouseEvent::which() const
 Node* MouseEvent::toElement() const
 {
     // MSIE extension - "the object toward which the user is moving the mouse pointer"
-    if (type() == eventNames().mouseoutEvent
+    if (type() == eventNames().mouseoutEvent || type() == eventNames().mouseleaveEvent)
         return relatedTarget() ? relatedTarget()->toNode() : 0;
     
     return target() ? target()->toNode() : 0;
@@ -211,7 +213,7 @@ Node* MouseEvent::toElement() const
 Node* MouseEvent::fromElement() const
 {
     // MSIE extension - "object from which activation or the mouse pointer is exiting during the event" (huh?)
-    if (type() != eventNames().mouseoutEvent)
+    if (type() != eventNames().mouseoutEvent && type() != eventNames().mouseleaveEvent)
         return relatedTarget() ? relatedTarget()->toNode() : 0;
     
     return target() ? target()->toNode() : 0;
index 9626b0c..17d7afe 100644 (file)
@@ -208,6 +208,8 @@ onloadeddata
 onloadedmetadata
 onloadstart
 onmousedown
+onmouseenter
+onmouseleave
 onmousemove
 onmouseout
 onmouseover
index 631d0ba..3449b3b 100644 (file)
@@ -226,6 +226,8 @@ AtomicString HTMLElement::eventNameForAttributeName(const QualifiedName& attrNam
         attributeNameToEventNameMap.set(oncontextmenuAttr.localName(), eventNames().contextmenuEvent);
         attributeNameToEventNameMap.set(ondblclickAttr.localName(), eventNames().dblclickEvent);
         attributeNameToEventNameMap.set(onmousedownAttr.localName(), eventNames().mousedownEvent);
+        attributeNameToEventNameMap.set(onmouseenterAttr.localName(), eventNames().mouseenterEvent);
+        attributeNameToEventNameMap.set(onmouseleaveAttr.localName(), eventNames().mouseleaveEvent);
         attributeNameToEventNameMap.set(onmousemoveAttr.localName(), eventNames().mousemoveEvent);
         attributeNameToEventNameMap.set(onmouseoutAttr.localName(), eventNames().mouseoutEvent);
         attributeNameToEventNameMap.set(onmouseoverAttr.localName(), eventNames().mouseoverEvent);
index e50582d..693f951 100644 (file)
@@ -316,6 +316,8 @@ namespace WebCore {
         DEFINE_ATTRIBUTE_EVENT_LISTENER(loadstart);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown);
+        DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter);
+        DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout);
         DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover);
index 46ec9d7..a2d9cac 100644 (file)
     attribute EventListener onloadstart;
     attribute EventListener onmessage;
     attribute EventListener onmousedown;
+    attribute EventListener onmouseenter;
+    attribute EventListener onmouseleave;
     attribute EventListener onmousemove;
     attribute EventListener onmouseout;
     attribute EventListener onmouseover;
index b124b7b..58e0593 100644 (file)
@@ -317,6 +317,10 @@ void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& v
         setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, name, value));
     else if (name == onmousedownAttr)
         setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, name, value));
+    else if (name == onmouseenterAttr)
+        setAttributeEventListener(eventNames().mouseenterEvent, createAttributeEventListener(this, name, value));
+    else if (name == onmouseleaveAttr)
+        setAttributeEventListener(eventNames().mouseleaveEvent, createAttributeEventListener(this, name, value));
     else if (name == onmousemoveAttr)
         setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, name, value));
     else if (name == onmouseoutAttr)
index 893f6f1..645ecf5 100644 (file)
@@ -116,6 +116,8 @@ public:
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), keyup);
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), load);
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mousedown);
+    DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mouseenter);
+    DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mouseleave);
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mousemove);
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mouseout);
     DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(correspondingElement(), mouseover);
index 0ef01c9..1c37501 100644 (file)
@@ -62,6 +62,8 @@
     attribute [NotEnumerable] EventListener onkeyup;
     attribute [NotEnumerable] EventListener onload;
     attribute [NotEnumerable] EventListener onmousedown;
+    attribute [NotEnumerable] EventListener onmouseenter;
+    attribute [NotEnumerable] EventListener onmouseleave;
     attribute [NotEnumerable] EventListener onmousemove;
     attribute [NotEnumerable] EventListener onmouseout;
     attribute [NotEnumerable] EventListener onmouseover;