Explorar el Código

LibWeb: Implement the `MouseEvent.relatedTarget` attribute

This returns the secondary target of a mouse event. For `onmouseenter`
and `onmouseover` events, this is the EventTarget the mouse exited
from. For `onmouseleave` and `onmouseout` events, this is the
EventTarget the mouse entered to.
Tim Ledbetter hace 1 año
padre
commit
a6d6729034

+ 10 - 10
Tests/LibWeb/Text/expected/UIEvents/mouse-events.txt

@@ -1,13 +1,13 @@
    > move pointer over #inner
-mouseover target.id=(inner) currentTarget.id=(inner)
-mouseover target.id=(inner) currentTarget.id=(outer)
-mouseenter target.id=(inner) currentTarget.id=(inner)
-mouseenter target.id=(outer) currentTarget.id=(outer)
+mouseover target.id=(inner) currentTarget.id=(inner), relatedTarget.id=(body)
+mouseover target.id=(inner) currentTarget.id=(outer), relatedTarget.id=(body)
+mouseenter target.id=(inner) currentTarget.id=(inner), relatedTarget.id=(body)
+mouseenter target.id=(outer) currentTarget.id=(outer), relatedTarget.id=(body)
 > move pointer over #outer
-mouseout target.id=(inner) currentTarget.id=(inner)
-mouseout target.id=(inner) currentTarget.id=(outer)
-mouseleave target.id=(inner) currentTarget.id=(inner)
-mouseover target.id=(outer) currentTarget.id=(outer)
+mouseout target.id=(inner) currentTarget.id=(inner), relatedTarget.id=(outer)
+mouseout target.id=(inner) currentTarget.id=(outer), relatedTarget.id=(outer)
+mouseleave target.id=(inner) currentTarget.id=(inner), relatedTarget.id=(outer)
+mouseover target.id=(outer) currentTarget.id=(outer), relatedTarget.id=(inner)
 > click document.body
-mouseout target.id=(outer) currentTarget.id=(outer)
-mouseleave target.id=(outer) currentTarget.id=(outer)
+mouseout target.id=(outer) currentTarget.id=(outer), relatedTarget.id=(body)
+mouseleave target.id=(outer) currentTarget.id=(outer), relatedTarget.id=(body)

+ 10 - 5
Tests/LibWeb/Text/input/UIEvents/mouse-events.html

@@ -3,6 +3,8 @@
 body {
     margin: 0px;
     padding: 5px;
+    width: 200px;
+    height: 200px;
 }
 
 #outer {
@@ -17,22 +19,23 @@ body {
     background-color: magenta;
 }
 </style>
-<div id="outer"><div id="inner"></div></div>
+<body id="body"><div id="outer"><div id="inner"></div></div></body>
 <script src="../include.js"></script>
 <script>
 function handleMouseOver(e) {
-    println(`mouseover target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id})`);
+    println(`mouseover target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id}), relatedTarget.id=(${e.relatedTarget.id})`);
 }
 function handleMouseOut(e) {
-    println(`mouseout target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id})`);
+    println(`mouseout target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id}), relatedTarget.id=(${e.relatedTarget.id})`);
 }
 function handleMouseEnter(e) {
-    println(`mouseenter target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id})`);
+    println(`mouseenter target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id}), relatedTarget.id=(${e.relatedTarget.id})`);
 }
 function handleMouseLeave(e) {
-    println(`mouseleave target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id})`);
+    println(`mouseleave target.id=(${e.target.id}) currentTarget.id=(${e.currentTarget.id}), relatedTarget.id=(${e.relatedTarget.id})`);
 }
 
+
 outer.onmouseover = handleMouseOver;
 outer.onmouseout = handleMouseOut;
 outer.onmouseenter = handleMouseEnter;
@@ -51,6 +54,8 @@ const clickOnBody = () => {
 }
 
 asyncTest(async done => {
+    // First move the mouse outside #outer to populate the MouseEvent.relatedTarget property
+    internals.movePointerTo(150, 150);
     println("> move pointer over #inner");
     internals.movePointerTo(10, 10);
     println("> move pointer over #outer");

+ 12 - 4
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -1337,7 +1337,9 @@ void Document::set_hovered_node(Node* node)
 
     // https://w3c.github.io/uievents/#mouseout
     if (old_hovered_node && old_hovered_node != m_hovered_node) {
-        auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseout);
+        UIEvents::MouseEventInit mouse_event_init {};
+        mouse_event_init.related_target = m_hovered_node;
+        auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseout, mouse_event_init);
         old_hovered_node->dispatch_event(event);
     }
 
@@ -1346,13 +1348,17 @@ void Document::set_hovered_node(Node* node)
         // FIXME: Check if we need to dispatch these events in a specific order.
         for (auto target = old_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
             // FIXME: Populate the event with mouse coordinates, etc.
-            target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseleave));
+            UIEvents::MouseEventInit mouse_event_init {};
+            mouse_event_init.related_target = m_hovered_node;
+            target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseleave, mouse_event_init));
         }
     }
 
     // https://w3c.github.io/uievents/#mouseover
     if (m_hovered_node && m_hovered_node != old_hovered_node) {
-        auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseover);
+        UIEvents::MouseEventInit mouse_event_init {};
+        mouse_event_init.related_target = old_hovered_node;
+        auto event = UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseover, mouse_event_init);
         m_hovered_node->dispatch_event(event);
     }
 
@@ -1361,7 +1367,9 @@ void Document::set_hovered_node(Node* node)
         // FIXME: Check if we need to dispatch these events in a specific order.
         for (auto target = m_hovered_node; target && target.ptr() != common_ancestor; target = target->parent()) {
             // FIXME: Populate the event with mouse coordinates, etc.
-            target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseenter));
+            UIEvents::MouseEventInit mouse_event_init {};
+            mouse_event_init.related_target = old_hovered_node;
+            target->dispatch_event(UIEvents::MouseEvent::create(realm(), UIEvents::EventNames::mouseenter, mouse_event_init));
         }
     }
 }

+ 7 - 0
Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp

@@ -45,6 +45,7 @@ MouseEvent::MouseEvent(JS::Realm& realm, FlyString const& event_name, MouseEvent
     , m_movement_y(event_init.movement_y)
     , m_button(event_init.button)
     , m_buttons(event_init.buttons)
+    , m_related_target(event_init.related_target)
 {
     set_event_characteristics();
 }
@@ -57,6 +58,12 @@ void MouseEvent::initialize(JS::Realm& realm)
     WEB_SET_PROTOTYPE_FOR_INTERFACE(MouseEvent);
 }
 
+void MouseEvent::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_related_target);
+}
+
 bool MouseEvent::get_modifier_state(String const& key_arg) const
 {
     if (key_arg == "Control")

+ 5 - 0
Userland/Libraries/LibWeb/UIEvents/MouseEvent.h

@@ -22,6 +22,7 @@ struct MouseEventInit : public EventModifierInit {
     double movement_y = 0;
     i16 button = 0;
     u16 buttons = 0;
+    JS::GCPtr<DOM::EventTarget> related_target = nullptr;
 };
 
 class MouseEvent : public UIEvent {
@@ -61,6 +62,8 @@ public:
     i16 button() const { return m_button; }
     u16 buttons() const { return m_buttons; }
 
+    JS::GCPtr<DOM::EventTarget> related_target() const { return m_related_target; }
+
     bool get_modifier_state(String const& key_arg) const;
 
     virtual u32 which() const override { return m_button + 1; }
@@ -69,6 +72,7 @@ protected:
     MouseEvent(JS::Realm&, FlyString const& event_name, MouseEventInit const& event_init, double page_x, double page_y, double offset_x, double offset_y);
 
     virtual void initialize(JS::Realm&) override;
+    virtual void visit_edges(Cell::Visitor&) override;
 
 private:
     virtual bool is_mouse_event() const override { return true; }
@@ -101,6 +105,7 @@ private:
     double m_movement_y { 0 };
     i16 m_button { 0 };
     u16 m_buttons { 0 };
+    JS::GCPtr<DOM::EventTarget> m_related_target { nullptr };
 };
 
 }

+ 2 - 2
Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl

@@ -29,7 +29,7 @@ interface MouseEvent : UIEvent {
     readonly attribute short button;
     readonly attribute unsigned short buttons;
 
-    [FIXME] readonly attribute EventTarget? relatedTarget;
+    readonly attribute EventTarget? relatedTarget;
 
     boolean getModifierState(DOMString keyArg);
 };
@@ -48,5 +48,5 @@ dictionary MouseEventInit : EventModifierInit {
 
     short button = 0;
     unsigned short buttons = 0;
-    // FIXME: EventTarget? relatedTarget = null;
+    EventTarget? relatedTarget = null;
 };