mirror of
https://github.com/SerenityOS/serenity
synced 2024-07-21 18:15:58 +00:00
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.
This commit is contained in:
parent
a33b9a8bca
commit
a7a5721149
|
@ -16,5 +16,33 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="my_div">Hello there!</div>
|
||||
|
||||
<div style="border: 1px solid black; width: 500px; height: 200px" id="divel">
|
||||
CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK
|
||||
ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME
|
||||
CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME CLICK ME
|
||||
</div>
|
||||
<p id="result">This text should be green, whether you click on the div border or the div text.</p>
|
||||
<p>
|
||||
<script>
|
||||
const divel = document.getElementById("divel");
|
||||
const result = document.getElementById("result");
|
||||
divel.addEventListener("click", event => {
|
||||
try {
|
||||
const text = `Result: Clicked on divel element with id ${event.target.getAttribute(
|
||||
"id"
|
||||
)}`;
|
||||
console.log(text);
|
||||
result.innerText = text;
|
||||
result.style.setProperty("color", "green");
|
||||
} catch (e) {
|
||||
const text = `Result: ${e.message}`;
|
||||
console.error(text);
|
||||
result.innerText = text;
|
||||
result.style.setProperty("color", "red");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Reference in a new issue