LibWeb: Chain wheel scrolling past iframe limits
Let wheel scrolling over iframes fall through to the parent document when the child document cannot make progress. This covers both iframes with no scrollable viewport and iframes that are already at their scroll boundary. Treat nested wheel events as terminal only when the child document actually consumes them or cancels them. For iframe viewport scrolling, check whether the child viewport position changed before reporting the wheel event as handled.
This commit is contained in:
parent
dfeb1e6b98
commit
8233200bc3
5 changed files with 109 additions and 5 deletions
|
|
@ -610,8 +610,10 @@ EventResult EventHandler::handle_mousewheel(CSSPixelPoint visual_viewport_positi
|
|||
if (auto result = dispatch_event_to_nested_navigable(*paintable, visual_viewport_position, [screen_position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y](EventHandler& event_handler, CSSPixelPoint position) -> EventResult {
|
||||
return event_handler.handle_mousewheel(position, screen_position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
|
||||
});
|
||||
result.has_value())
|
||||
return result.value();
|
||||
result.has_value()) {
|
||||
if (result.value() == EventResult::Handled || result.value() == EventResult::Cancelled)
|
||||
return result.value();
|
||||
}
|
||||
|
||||
// NB: Search for the first parent of the hit target that's an element.
|
||||
GC::Ptr<Layout::Node> layout_node;
|
||||
|
|
@ -622,11 +624,19 @@ EventResult EventHandler::handle_mousewheel(CSSPixelPoint visual_viewport_positi
|
|||
auto const& offset_paintable = layout_node->first_paintable() ? layout_node->first_paintable() : paintable.ptr();
|
||||
auto scroll_offset = document->navigable()->viewport_scroll_offset();
|
||||
auto offset = compute_mouse_event_offset(visual_viewport_position.translated(scroll_offset), *offset_paintable);
|
||||
bool could_scroll_viewport = document->paintable_box()->could_be_scrolled_by_wheel_event();
|
||||
if (node->dispatch_event(UIEvents::WheelEvent::create_from_platform_event(node->realm(), m_navigable->active_window_proxy(), UIEvents::EventNames::wheel, screen_position, page_offset, viewport_position, offset, wheel_delta_x, wheel_delta_y, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors())) {
|
||||
m_navigable->scroll_viewport_by_delta({ wheel_delta_x, wheel_delta_y });
|
||||
if (could_scroll_viewport) {
|
||||
auto viewport_scroll_position_before = CSSPixelPoint { CSSPixels(document->visual_viewport()->page_left()), CSSPixels(document->visual_viewport()->page_top()) };
|
||||
m_navigable->scroll_viewport_by_delta({ wheel_delta_x, wheel_delta_y });
|
||||
auto viewport_scroll_position_after = CSSPixelPoint { CSSPixels(document->visual_viewport()->page_left()), CSSPixels(document->visual_viewport()->page_top()) };
|
||||
handled_event = viewport_scroll_position_before != viewport_scroll_position_after ? EventResult::Handled : EventResult::Accepted;
|
||||
} else {
|
||||
handled_event = EventResult::Accepted;
|
||||
}
|
||||
} else {
|
||||
handled_event = EventResult::Cancelled;
|
||||
}
|
||||
|
||||
handled_event = EventResult::Handled;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
iframe.scrollTop before wheel: 450
|
||||
iframe.scrollHeight: 600
|
||||
iframe.clientHeight: 150
|
||||
window.scrollY: 100
|
||||
iframe.scrollTop after wheel: 450
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
window.scrollY: 100
|
||||
iframe.scrollY: 0
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="include.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#iframe {
|
||||
display: block;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#spacer {
|
||||
height: 2000px;
|
||||
background: linear-gradient(white, orange);
|
||||
}
|
||||
</style>
|
||||
<div id="spacer"></div>
|
||||
<script>
|
||||
promiseTest(async () => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.id = "iframe";
|
||||
iframe.srcdoc = `
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
html { scrollbar-width: none; }
|
||||
</style>
|
||||
<div style="width: 200px; height: 200px; background-color: darkblue;"></div>
|
||||
<div style="width: 200px; height: 200px; background-color: blue;"></div>
|
||||
<div style="width: 200px; height: 200px; background-color: magenta;"></div>
|
||||
`;
|
||||
document.body.prepend(iframe);
|
||||
|
||||
await new Promise(resolve => iframe.addEventListener("load", resolve, { once: true }));
|
||||
|
||||
const scrollingElement = iframe.contentDocument.scrollingElement;
|
||||
scrollingElement.scrollTop = 10000;
|
||||
await animationFrame();
|
||||
|
||||
println(`iframe.scrollTop before wheel: ${scrollingElement.scrollTop}`);
|
||||
println(`iframe.scrollHeight: ${scrollingElement.scrollHeight}`);
|
||||
println(`iframe.clientHeight: ${scrollingElement.clientHeight}`);
|
||||
|
||||
internals.wheel(50, 50, 0, 100);
|
||||
await animationFrame();
|
||||
|
||||
println(`window.scrollY: ${window.scrollY}`);
|
||||
println(`iframe.scrollTop after wheel: ${scrollingElement.scrollTop}`);
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="include.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
display: block;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#spacer {
|
||||
height: 2000px;
|
||||
background: linear-gradient(white, orange);
|
||||
}
|
||||
</style>
|
||||
<iframe id="iframe" srcdoc="<!DOCTYPE html><body style='margin: 0; height: 50px; background: lightblue;'>iframe</body>"></iframe>
|
||||
<div id="spacer"></div>
|
||||
<script>
|
||||
promiseTest(async () => {
|
||||
const iframe = document.getElementById("iframe");
|
||||
|
||||
await new Promise(resolve => iframe.addEventListener("load", resolve, { once: true }));
|
||||
|
||||
internals.wheel(50, 50, 0, 100);
|
||||
await animationFrame();
|
||||
|
||||
println(`window.scrollY: ${window.scrollY}`);
|
||||
println(`iframe.scrollY: ${iframe.contentWindow.scrollY}`);
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue