summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorkleines Filmröllchen <filmroellchen@serenityos.org>2022-05-03 16:50:38 +0200
committerLinus Groh <mail@linusgroh.de>2022-06-05 22:31:06 +0100
commita7a5721149427352845bc0a020f625da9bb02a78 (patch)
tree066d9509f2dbd7de4b0d7d61842d1e9ec46d43d0 /Userland
parenta33b9a8bca838df09dc945be536624ecf0e66392 (diff)
downloadserenity-a7a5721149427352845bc0a020f625da9bb02a78.zip
LibWeb: Dispatch mouse events to topmost element instead of hit target
This improves our spec compliance by allowing the user to click non-element nodes (like text) and having the click be registered with the parent element (like a div or button). This makes Fandom's cookie accept button work if you click the text. Additionally, the events test page contains a test to check the target element, which would previously not exist when we fired the event at a non-element.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/Page/EventHandler.cpp46
1 files changed, 43 insertions, 3 deletions
diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
index 24166b3fb0..f5a098b6ee 100644
--- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp
+++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
@@ -190,7 +190,21 @@ bool EventHandler::handle_mouseup(Gfx::IntPoint const& position, unsigned button
return nested_browsing_context->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), button, modifiers);
return false;
}
- auto offset = compute_mouse_event_offset(position, paintable->layout_node());
+
+ // Search for the first parent of the hit target that's an element.
+ // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
+ // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
+ auto* layout_node = &paintable->layout_node();
+ while (layout_node && node && !node->is_element() && layout_node->parent()) {
+ layout_node = layout_node->parent();
+ node = layout_node->dom_node();
+ }
+ if (!node || !layout_node) {
+ // FIXME: This is pretty ugly but we need to bail out here.
+ goto after_node_use;
+ }
+
+ auto offset = compute_mouse_event_offset(position, *layout_node);
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(UIEvents::EventNames::mouseup, offset.x(), offset.y(), position.x(), position.y(), button));
handled_event = true;
@@ -251,6 +265,7 @@ bool EventHandler::handle_mouseup(Gfx::IntPoint const& position, unsigned button
}
}
+after_node_use:
if (button == GUI::MouseButton::Primary)
m_in_mouse_selection = false;
return handled_event;
@@ -306,8 +321,19 @@ bool EventHandler::handle_mousedown(Gfx::IntPoint const& position, unsigned butt
if (auto* page = m_browsing_context.page())
page->set_focused_browsing_context({}, m_browsing_context);
+ // Search for the first parent of the hit target that's an element.
+ // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
+ // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
+ auto* layout_node = &paintable->layout_node();
+ while (layout_node && node && !node->is_element() && layout_node->parent()) {
+ layout_node = layout_node->parent();
+ node = layout_node->dom_node();
+ }
+ if (!node || !layout_node)
+ return false;
+
m_mousedown_target = node;
- auto offset = compute_mouse_event_offset(position, paintable->layout_node());
+ auto offset = compute_mouse_event_offset(position, *layout_node);
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(UIEvents::EventNames::mousedown, offset.x(), offset.y(), position.x(), position.y(), button));
}
@@ -415,12 +441,26 @@ bool EventHandler::handle_mousemove(Gfx::IntPoint const& position, unsigned butt
hovered_node_cursor = cursor_css_to_gfx(cursor);
}
- auto offset = compute_mouse_event_offset(position, paintable->layout_node());
+ // Search for the first parent of the hit target that's an element.
+ // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
+ // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
+ auto* layout_node = &paintable->layout_node();
+ while (layout_node && node && !node->is_element() && layout_node->parent()) {
+ layout_node = layout_node->parent();
+ node = layout_node->dom_node();
+ }
+ if (!node || !layout_node) {
+ // FIXME: This is pretty ugly but we need to bail out here.
+ goto after_node_use;
+ }
+
+ auto offset = compute_mouse_event_offset(position, *layout_node);
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(UIEvents::EventNames::mousemove, offset.x(), offset.y(), position.x(), position.y()));
// NOTE: Dispatching an event may have disturbed the world.
if (!paint_root() || paint_root() != node->document().paint_box())
return true;
}
+ after_node_use:
if (m_in_mouse_selection) {
auto hit = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::TextCursor);
if (start_index.has_value() && hit.has_value() && hit->dom_node()) {