LibWeb: Don't class mousewheel as handled if scroll offset isn't updated

Before this change, you could only scroll the current hovered scroll
container, even if it was at the beginning or end and thus having no
effect.

Now, if it doesn't update, it will not be classed as handled and will
move onto the next scroll container.
This commit is contained in:
Luke Wilde 2025-10-07 14:09:30 +01:00 committed by Alexander Kalenik
commit adeedabf54
Notes: github-actions[bot] 2025-10-07 17:44:30 +00:00
4 changed files with 22 additions and 16 deletions

View file

@ -1867,7 +1867,7 @@ void Element::set_scroll_left(double x)
// FIXME: Implement this in terms of calling "scroll the element". // FIXME: Implement this in terms of calling "scroll the element".
auto scroll_offset = paintable_box()->scroll_offset(); auto scroll_offset = paintable_box()->scroll_offset();
scroll_offset.set_x(CSSPixels::nearest_value_for(x)); scroll_offset.set_x(CSSPixels::nearest_value_for(x));
paintable_box()->set_scroll_offset(scroll_offset); (void)paintable_box()->set_scroll_offset(scroll_offset);
} }
void Element::set_scroll_top(double y) void Element::set_scroll_top(double y)
@ -1924,7 +1924,7 @@ void Element::set_scroll_top(double y)
// FIXME: Implement this in terms of calling "scroll the element". // FIXME: Implement this in terms of calling "scroll the element".
auto scroll_offset = paintable_box()->scroll_offset(); auto scroll_offset = paintable_box()->scroll_offset();
scroll_offset.set_y(CSSPixels::nearest_value_for(y)); scroll_offset.set_y(CSSPixels::nearest_value_for(y));
paintable_box()->set_scroll_offset(scroll_offset); (void)paintable_box()->set_scroll_offset(scroll_offset);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth // https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
@ -3144,7 +3144,7 @@ void Element::scroll(double x, double y)
auto scroll_offset = paintable_box()->scroll_offset(); auto scroll_offset = paintable_box()->scroll_offset();
scroll_offset.set_x(CSSPixels::nearest_value_for(x)); scroll_offset.set_x(CSSPixels::nearest_value_for(x));
scroll_offset.set_y(CSSPixels::nearest_value_for(y)); scroll_offset.set_y(CSSPixels::nearest_value_for(y));
paintable_box()->set_scroll_offset(scroll_offset); (void)paintable_box()->set_scroll_offset(scroll_offset);
} }
// https://drafts.csswg.org/cssom-view/#dom-element-scroll // https://drafts.csswg.org/cssom-view/#dom-element-scroll

View file

@ -433,7 +433,7 @@ void LayoutState::commit(Box& root)
// (scrollable overflow rect become smaller), the scroll offset would be out of bounds. // (scrollable overflow rect become smaller), the scroll offset would be out of bounds.
auto& paintable_box = const_cast<Painting::PaintableBox&>(*box->paintable_box()); auto& paintable_box = const_cast<Painting::PaintableBox&>(*box->paintable_box());
if (!paintable_box.scroll_offset().is_zero()) if (!paintable_box.scroll_offset().is_zero())
paintable_box.set_scroll_offset(paintable_box.scroll_offset()); (void)paintable_box.set_scroll_offset(paintable_box.scroll_offset());
} }
for (auto& it : used_values_per_layout_node) { for (auto& it : used_values_per_layout_node) {

View file

@ -111,11 +111,11 @@ CSSPixelPoint PaintableBox::scroll_offset() const
return static_cast<DOM::Element const*>(dom_node())->scroll_offset({}); return static_cast<DOM::Element const*>(dom_node())->scroll_offset({});
} }
void PaintableBox::set_scroll_offset(CSSPixelPoint offset) PaintableBox::ScrollHandled PaintableBox::set_scroll_offset(CSSPixelPoint offset)
{ {
auto scrollable_overflow_rect = this->scrollable_overflow_rect(); auto scrollable_overflow_rect = this->scrollable_overflow_rect();
if (!scrollable_overflow_rect.has_value()) if (!scrollable_overflow_rect.has_value())
return; return ScrollHandled::No;
document().set_needs_to_refresh_scroll_state(true); document().set_needs_to_refresh_scroll_state(true);
@ -128,7 +128,7 @@ void PaintableBox::set_scroll_offset(CSSPixelPoint offset)
// FIXME: If there is horizontal and vertical scroll ignore only part of the new offset // FIXME: If there is horizontal and vertical scroll ignore only part of the new offset
if (offset.y() < 0 || scroll_offset() == offset) if (offset.y() < 0 || scroll_offset() == offset)
return; return ScrollHandled::No;
auto& node = layout_node(); auto& node = layout_node();
if (auto pseudo_element = node.generated_for_pseudo_element(); pseudo_element.has_value()) { if (auto pseudo_element = node.generated_for_pseudo_element(); pseudo_element.has_value()) {
@ -136,7 +136,7 @@ void PaintableBox::set_scroll_offset(CSSPixelPoint offset)
} else if (auto* element = as_if<DOM::Element>(dom_node())) { } else if (auto* element = as_if<DOM::Element>(dom_node())) {
element->set_scroll_offset({}, offset); element->set_scroll_offset({}, offset);
} else { } else {
return; return ScrollHandled::No;
} }
// https://drafts.csswg.org/cssom-view-1/#scrolling-events // https://drafts.csswg.org/cssom-view-1/#scrolling-events
@ -154,17 +154,18 @@ void PaintableBox::set_scroll_offset(CSSPixelPoint offset)
// 3. If the element is already in docs pending scroll event targets, abort these steps. // 3. If the element is already in docs pending scroll event targets, abort these steps.
if (document.pending_scroll_event_targets().contains_slow(event_target)) if (document.pending_scroll_event_targets().contains_slow(event_target))
return; return ScrollHandled::Yes;
// 4. Append the element to docs pending scroll event targets. // 4. Append the element to docs pending scroll event targets.
document.pending_scroll_event_targets().append(*layout_node_with_style_and_box_metrics().dom_node()); document.pending_scroll_event_targets().append(*layout_node_with_style_and_box_metrics().dom_node());
set_needs_display(InvalidateDisplayList::No); set_needs_display(InvalidateDisplayList::No);
return ScrollHandled::Yes;
} }
void PaintableBox::scroll_by(int delta_x, int delta_y) PaintableBox::ScrollHandled PaintableBox::scroll_by(int delta_x, int delta_y)
{ {
set_scroll_offset(scroll_offset().translated(delta_x, delta_y)); return set_scroll_offset(scroll_offset().translated(delta_x, delta_y));
} }
void PaintableBox::set_offset(CSSPixelPoint offset) void PaintableBox::set_offset(CSSPixelPoint offset)
@ -1163,7 +1164,7 @@ void PaintableBox::scroll_to_mouse_position(CSSPixelPoint position)
if (is_viewport()) if (is_viewport())
document().navigable()->perform_scroll_of_viewport(new_scroll_offset); document().navigable()->perform_scroll_of_viewport(new_scroll_offset);
else else
set_scroll_offset(new_scroll_offset); (void)set_scroll_offset(new_scroll_offset);
} }
bool PaintableBox::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) bool PaintableBox::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y)
@ -1173,8 +1174,8 @@ bool PaintableBox::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigne
return false; return false;
} }
scroll_by(wheel_delta_x, wheel_delta_y); auto scroll_handled = scroll_by(wheel_delta_x, wheel_delta_y);
return true; return scroll_handled == ScrollHandled::Yes;
} }
TraversalDecision PaintableBox::hit_test_scrollbars(CSSPixelPoint position, Function<TraversalDecision(HitTestResult)> const& callback) const TraversalDecision PaintableBox::hit_test_scrollbars(CSSPixelPoint position, Function<TraversalDecision(HitTestResult)> const& callback) const

View file

@ -63,9 +63,14 @@ public:
// Offset from the top left of the containing block's content edge. // Offset from the top left of the containing block's content edge.
[[nodiscard]] CSSPixelPoint offset() const; [[nodiscard]] CSSPixelPoint offset() const;
enum class ScrollHandled {
No,
Yes,
};
CSSPixelPoint scroll_offset() const; CSSPixelPoint scroll_offset() const;
void set_scroll_offset(CSSPixelPoint); [[nodiscard]] ScrollHandled set_scroll_offset(CSSPixelPoint);
void scroll_by(int delta_x, int delta_y); [[nodiscard]] ScrollHandled scroll_by(int delta_x, int delta_y);
void set_offset(CSSPixelPoint); void set_offset(CSSPixelPoint);
void set_offset(float x, float y) { set_offset({ x, y }); } void set_offset(float x, float y) { set_offset({ x, y }); }