Skip to content

Commit 96806a3

Browse files
trflynn89kalenikaliaksandr
authored andcommitted
LibWeb: Update cursor and tooltip UI state regardless of event handlers
If a script on the page cancels a mousemove event, we would return early and neglect to update the cursor. This is seen regularly on "Diablo Web" where they set the cursor to "none" on the main canvas, and also cancel mousemove events.
1 parent 6125581 commit 96806a3

File tree

6 files changed

+94
-38
lines changed

6 files changed

+94
-38
lines changed

Libraries/LibWeb/Internals/Internals.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include <AK/JsonObject.h>
9+
#include <LibGfx/Cursor.h>
910
#include <LibJS/Runtime/Date.h>
1011
#include <LibJS/Runtime/VM.h>
1112
#include <LibUnicode/TimeZone.h>
@@ -236,6 +237,20 @@ void Internals::pinch(double x, double y, double scale_delta)
236237
page.handle_pinch_event(position, scale_delta);
237238
}
238239

240+
String Internals::current_cursor()
241+
{
242+
auto& page = this->page();
243+
244+
return page.current_cursor().visit(
245+
[](Gfx::StandardCursor cursor) {
246+
auto cursor_string = Gfx::standard_cursor_to_string(cursor);
247+
return String::from_utf8_without_validation(cursor_string.bytes());
248+
},
249+
[](Gfx::ImageCursor const&) {
250+
return "Image"_string;
251+
});
252+
}
253+
239254
WebIDL::ExceptionOr<bool> Internals::dispatch_user_activated_event(DOM::EventTarget& target, DOM::Event& event)
240255
{
241256
event.set_is_trusted(true);

Libraries/LibWeb/Internals/Internals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class WEB_API Internals final : public InternalsBase {
4343
void wheel(double x, double y, double delta_x, double delta_y);
4444
void pinch(double x, double y, double scale_delta);
4545

46+
String current_cursor();
47+
4648
WebIDL::ExceptionOr<bool> dispatch_user_activated_event(DOM::EventTarget&, DOM::Event& event);
4749

4850
void spoof_current_url(String const& url);

Libraries/LibWeb/Internals/Internals.idl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ interface Internals {
3535
undefined wheel(double x, double y, double deltaX, double deltaY);
3636
undefined pinch(double x, double y, double scaleDelta);
3737

38+
DOMString currentCursor();
39+
3840
boolean dispatchUserActivatedEvent(EventTarget target, Event event);
3941
undefined spoofCurrentURL(USVString url);
4042

Libraries/LibWeb/Page/EventHandler.cpp

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,8 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
748748
return EventResult::Dropped;
749749

750750
auto& document = *m_navigable->active_document();
751+
auto& page = m_navigable->page();
752+
751753
auto viewport_position = document.visual_viewport()->map_to_layout_viewport(visual_viewport_position);
752754

753755
m_navigable->active_document()->update_layout(DOM::UpdateLayoutReason::EventHandlerHandleMouseMove);
@@ -756,8 +758,8 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
756758
return EventResult::Dropped;
757759

758760
bool hovered_node_changed = false;
759-
bool is_hovering_link = false;
760761
Gfx::Cursor hovered_node_cursor = Gfx::StandardCursor::None;
762+
GC::Ptr<HTML::HTMLAnchorElement const> hovered_link_element;
761763

762764
GC::Ptr<Painting::Paintable> paintable;
763765
Optional<int> start_index;
@@ -769,11 +771,42 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
769771

770772
GC::Ptr<DOM::Node> node;
771773

772-
ScopeGuard update_hovered_node_guard = [&node, &document] {
774+
ScopeGuard update_hovered_node_and_ui_state_guard = [&] {
773775
document.set_hovered_node(node);
776+
777+
// FIXME: This check is only approximate. ImageCursors from the same CursorStyleValue share bitmaps, but may
778+
// repaint them. So comparing them does not tell you if they are the same image. Also, the image may
779+
// change even if the hovered node does not.
780+
if (page.current_cursor() != hovered_node_cursor || hovered_node_changed) {
781+
page.client().page_did_request_cursor_change(hovered_node_cursor);
782+
page.set_current_cursor(hovered_node_cursor);
783+
}
784+
785+
if (hovered_node_changed) {
786+
GC::Ptr<HTML::HTMLElement const> hovered_html_element = node
787+
? node->enclosing_html_element_with_attribute(HTML::AttributeNames::title)
788+
: nullptr;
789+
790+
if (hovered_html_element && hovered_html_element->title().has_value()) {
791+
page.client().page_did_enter_tooltip_area(hovered_html_element->title()->to_byte_string());
792+
page.set_is_in_tooltip_area(true);
793+
} else if (page.is_in_tooltip_area()) {
794+
page.client().page_did_leave_tooltip_area();
795+
page.set_is_in_tooltip_area(false);
796+
}
797+
798+
if (hovered_link_element) {
799+
if (auto link_url = document.encoding_parse_url(hovered_link_element->href()); link_url.has_value()) {
800+
page.client().page_did_hover_link(*link_url);
801+
page.set_is_hovering_link(true);
802+
}
803+
} else if (page.is_hovering_link()) {
804+
page.client().page_did_unhover_link();
805+
page.set_is_hovering_link(false);
806+
}
807+
}
774808
};
775809

776-
GC::Ptr<HTML::HTMLAnchorElement const> hovered_link_element;
777810
if (paintable) {
778811
if (paintable->wants_mouse_events()) {
779812
if (paintable->handle_mousemove({}, viewport_position, buttons, modifiers) == Painting::Paintable::DispatchEventOfSameName::No) {
@@ -782,7 +815,7 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
782815
}
783816

784817
// FIXME: It feels a bit aggressive to always update the cursor like this.
785-
m_navigable->page().client().page_did_request_cursor_change(Gfx::StandardCursor::None);
818+
page.client().page_did_request_cursor_change(Gfx::StandardCursor::None);
786819
}
787820

788821
node = dom_node_for_event_dispatch(*paintable);
@@ -804,10 +837,9 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
804837
GC::Ptr<Layout::Node> layout_node;
805838
bool found_parent_element = parent_element_for_event_dispatch(*paintable, node, layout_node);
806839
hovered_node_changed = node.ptr() != document.hovered_node();
840+
807841
if (found_parent_element) {
808842
hovered_link_element = node->enclosing_link_element();
809-
if (hovered_link_element)
810-
is_hovering_link = true;
811843

812844
if (paintable->layout_node().is_text_node()) {
813845
hovered_node_cursor = resolve_cursor(*paintable->layout_node().parent(), cursor_data, Gfx::StandardCursor::IBeam);
@@ -856,38 +888,6 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
856888
}
857889
}
858890

859-
auto& page = m_navigable->page();
860-
861-
// FIXME: This check is only approximate. ImageCursors from the same CursorStyleValue share bitmaps, but may repaint them.
862-
// So comparing them does not tell you if they are the same image. Also, the image may change even if the hovered
863-
// node does not.
864-
if (page.current_cursor() != hovered_node_cursor || hovered_node_changed) {
865-
page.set_current_cursor(hovered_node_cursor);
866-
page.client().page_did_request_cursor_change(hovered_node_cursor);
867-
}
868-
869-
if (hovered_node_changed) {
870-
GC::Ptr<HTML::HTMLElement const> hovered_html_element = node ? node->enclosing_html_element_with_attribute(HTML::AttributeNames::title) : nullptr;
871-
872-
if (hovered_html_element && hovered_html_element->title().has_value()) {
873-
page.set_is_in_tooltip_area(true);
874-
page.client().page_did_enter_tooltip_area(hovered_html_element->title()->to_byte_string());
875-
} else if (page.is_in_tooltip_area()) {
876-
page.set_is_in_tooltip_area(false);
877-
page.client().page_did_leave_tooltip_area();
878-
}
879-
880-
if (is_hovering_link) {
881-
if (auto link_url = document.encoding_parse_url(hovered_link_element->href()); link_url.has_value()) {
882-
page.set_is_hovering_link(true);
883-
page.client().page_did_hover_link(*link_url);
884-
}
885-
} else if (page.is_hovering_link()) {
886-
page.set_is_hovering_link(false);
887-
page.client().page_did_unhover_link();
888-
}
889-
}
890-
891891
return EventResult::Handled;
892892
}
893893

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Arrow
2+
OpenHand
3+
Arrow
4+
OpenHand
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<style>
3+
#foo {
4+
width: 100px;
5+
height: 100px;
6+
7+
position: absolute;
8+
top: 20px;
9+
left: 20px;
10+
11+
cursor: grab;
12+
background-color: red;
13+
}
14+
</style>
15+
<div id="foo"></div>
16+
<script src="../include.js"></script>
17+
<script>
18+
test(() => {
19+
internals.movePointerTo(0, 0);
20+
println(internals.currentCursor());
21+
22+
internals.movePointerTo(100, 100);
23+
println(internals.currentCursor());
24+
25+
foo.addEventListener("mousemove", e => e.preventDefault());
26+
27+
internals.movePointerTo(0, 0);
28+
println(internals.currentCursor());
29+
30+
internals.movePointerTo(100, 100);
31+
println(internals.currentCursor());
32+
});
33+
</script>

0 commit comments

Comments
 (0)