Bug 1849229 - [remote] Convert delta values for wheel scroll actions from CSS to device pixels. r=webdriver-reviewers,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D186534
This commit is contained in:
Henrik Skupin 2023-08-21 15:21:46 +00:00
parent 68a7302612
commit 08de5855e6
6 changed files with 252 additions and 162 deletions

View File

@ -157,6 +157,20 @@ event.synthesizeTouchAtPoint = function (left, top, opts, win) {
* Window object.
*/
event.synthesizeWheelAtPoint = function (left, top, opts, win) {
const dpr = win.devicePixelRatio;
// All delta properties expect the value in device pixels while the
// WebDriver specification uses CSS pixels.
if (typeof opts.deltaX !== "undefined") {
opts.deltaX *= dpr;
}
if (typeof opts.deltaY !== "undefined") {
opts.deltaY *= dpr;
}
if (typeof opts.deltaZ !== "undefined") {
opts.deltaZ *= dpr;
}
return _getEventUtils(win).synthesizeWheelAtPoint(left, top, opts, win);
};

View File

@ -0,0 +1,37 @@
from copy import deepcopy
import pytest
from tests.classic.perform_actions.support.refine import get_events
@pytest.mark.parametrize("device_pixel_ratio", ["1.0", "2.0", "0.5"])
def test_scroll_delta_device_pixel(configuration, url, geckodriver, device_pixel_ratio):
config = deepcopy(configuration)
prefs = config["capabilities"]["moz:firefoxOptions"].get("prefs", {})
prefs.update({"layout.css.devPixelsPerPx": device_pixel_ratio})
config["capabilities"]["moz:firefoxOptions"]["prefs"] = prefs
try:
driver = geckodriver(config=config)
driver.new_session()
driver.session.url = url(
"/webdriver/tests/support/html/test_actions_scroll.html"
)
target = driver.session.find.css("#scrollable", all=False)
chain = driver.session.actions.sequence("wheel", "wheel_id")
chain.scroll(0, 0, 5, 10, origin=target).perform()
events = get_events(driver.session)
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] == 5
assert events[0]["deltaY"] == 10
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "scrollable-content"
finally:
driver.stop()

View File

@ -10,14 +10,14 @@ pytestmark = pytest.mark.asyncio
@pytest.mark.parametrize("delta_x, delta_y", [(0, 10), (5, 0), (5, 10)])
async def test_wheel_scroll(
async def test_scroll_not_scrollable(
bidi_session, setup_wheel_test, top_context, get_element, delta_x, delta_y
):
actions = Actions()
outer = await get_element("#outer")
target = await get_element("#not-scrollable")
actions.add_wheel().scroll(
x=0, y=0, delta_x=delta_x, delta_y=delta_y, origin=get_element_origin(outer)
x=0, y=0, delta_x=delta_x, delta_y=delta_y, origin=get_element_origin(target)
)
await bidi_session.input.perform_actions(
@ -27,37 +27,14 @@ async def test_wheel_scroll(
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= delta_x
assert events[0]["deltaY"] >= delta_y
assert events[0]["deltaX"] == delta_x
assert events[0]["deltaY"] == delta_y
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "outer"
assert events[0]["target"] == "not-scrollable-content"
@pytest.mark.parametrize("delta_x, delta_y", [(0, 10), (5, 0), (5, 10)])
async def test_wheel_scroll_iframe(
bidi_session, setup_wheel_test, top_context, get_element, delta_x, delta_y
):
actions = Actions()
subframe = await get_element("#subframe")
actions.add_wheel().scroll(
x=0, y=0, delta_x=delta_x, delta_y=delta_y, origin=get_element_origin(subframe)
)
await bidi_session.input.perform_actions(
actions=actions, context=top_context["context"]
)
events = await get_events(bidi_session, top_context["context"])
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= delta_x
assert events[0]["deltaY"] >= delta_y
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "iframeContent"
@pytest.mark.parametrize("delta_x, delta_y", [(0, 10), (5, 0), (5, 10)])
async def test_wheel_scroll_overflow(
async def test_scroll_scrollable_overflow(
bidi_session, setup_wheel_test, top_context, get_element, delta_x, delta_y
):
actions = Actions()
@ -78,15 +55,38 @@ async def test_wheel_scroll_overflow(
events = await get_events(bidi_session, top_context["context"])
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= delta_x
assert events[0]["deltaY"] >= delta_y
assert events[0]["deltaX"] == delta_x
assert events[0]["deltaY"] == delta_y
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "scrollContent"
assert events[0]["target"] == "scrollable-content"
@pytest.mark.parametrize("delta_x, delta_y", [(0, 10), (5, 0), (5, 10)])
async def test_scroll_iframe(
bidi_session, setup_wheel_test, top_context, get_element, delta_x, delta_y
):
actions = Actions()
target = await get_element("#iframe")
actions.add_wheel().scroll(
x=0, y=0, delta_x=delta_x, delta_y=delta_y, origin=get_element_origin(target)
)
await bidi_session.input.perform_actions(
actions=actions, context=top_context["context"]
)
events = await get_events(bidi_session, top_context["context"])
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] == delta_x
assert events[0]["deltaY"] == delta_y
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "iframeContent"
@pytest.mark.parametrize("mode", ["open", "closed"])
@pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
async def test_wheel_scroll_shadow_tree(
async def test_scroll_shadow_tree(
bidi_session, top_context, get_test_page, mode, nested
):
await bidi_session.browsing_context.navigate(

View File

@ -2,6 +2,8 @@
import pytest
from webdriver.error import InvalidArgumentException
from tests.support.asserts import assert_error
from . import perform_actions
@ -821,3 +823,16 @@ def test_wheel_action_scroll_origin_element_invalid_value(session):
]
response = perform_actions(session, actions)
assert_error(response, "no such element")
@pytest.mark.parametrize("missing", ["x", "y", "deltaX", "deltaY"])
def test_wheel_action_scroll_missing_property(
session, test_actions_scroll_page, wheel_chain, missing
):
target = session.find.css("#scrollable", all=False)
actions = wheel_chain.scroll(0, 0, 5, 10, origin=target)
del actions._actions[-1][missing]
with pytest.raises(InvalidArgumentException):
actions.perform()

View File

@ -1,10 +1,9 @@
import pytest
from webdriver.error import InvalidArgumentException, NoSuchWindowException
from webdriver.error import NoSuchWindowException
from tests.classic.perform_actions.support.refine import get_events
from tests.support.asserts import assert_move_to_coordinates
from tests.support.helpers import filter_dict
def test_null_response_value(session, wheel_chain):
@ -22,51 +21,51 @@ def test_no_browsing_context(session, closed_window, wheel_chain):
wheel_chain.scroll(0, 0, 0, 10).perform()
def test_wheel_scroll(session, test_actions_scroll_page, wheel_chain):
session.execute_script("document.scrollingElement.scrollTop = 0")
def test_scroll_not_scrollable(session, test_actions_scroll_page, wheel_chain):
target = session.find.css("#not-scrollable", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=target).perform()
outer = session.find.css("#outer", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=outer).perform()
events = get_events(session)
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= 5
assert events[0]["deltaY"] >= 10
assert events[0]["deltaX"] == 5
assert events[0]["deltaY"] == 10
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "outer"
assert events[0]["target"] == "not-scrollable-content"
def test_wheel_scroll_overflow(session, test_actions_scroll_page, wheel_chain):
session.execute_script("document.scrollingElement.scrollTop = 0")
def test_scroll_scrollable_overflow(session, test_actions_scroll_page, wheel_chain):
target = session.find.css("#scrollable", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=target).perform()
scrollable = session.find.css("#scrollable", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=scrollable).perform()
events = get_events(session)
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= 5
assert events[0]["deltaY"] >= 10
assert events[0]["deltaX"] == 5
assert events[0]["deltaY"] == 10
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "scrollContent"
assert events[0]["target"] == "scrollable-content"
def test_wheel_scroll_iframe(session, test_actions_scroll_page, wheel_chain):
session.execute_script("document.scrollingElement.scrollTop = 0")
def test_scroll_iframe(session, test_actions_scroll_page, wheel_chain):
target = session.find.css("#iframe", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=target).perform()
subframe = session.find.css("#subframe", all=False)
wheel_chain.scroll(0, 0, 5, 10, origin=subframe).perform()
events = get_events(session)
assert len(events) == 1
assert events[0]["type"] == "wheel"
assert events[0]["deltaX"] >= 5
assert events[0]["deltaY"] >= 10
assert events[0]["deltaX"] == 5
assert events[0]["deltaY"] == 10
assert events[0]["deltaZ"] == 0
assert events[0]["target"] == "iframeContent"
@pytest.mark.parametrize("mode", ["open", "closed"])
@pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
def test_wheel_scroll_shadow_tree(session, get_test_page, wheel_chain, mode, nested):
def test_scroll_shadow_tree(session, get_test_page, wheel_chain, mode, nested):
session.url = get_test_page(
shadow_doc="""
<div id="scrollableShadowTree"
@ -109,17 +108,6 @@ def test_wheel_scroll_shadow_tree(session, get_test_page, wheel_chain, mode, nes
events = session.execute_script("return window.wheelEvents;") or []
assert len(events) == 1
assert events[0]["deltaX"] >= 5
assert events[0]["deltaY"] >= 10
assert events[0]["deltaX"] == 5
assert events[0]["deltaY"] == 10
assert events[0]["target"] == "scrollableShadowTreeContent"
@pytest.mark.parametrize("missing", ["x", "y", "deltaX", "deltaY"])
def test_wheel_missing_prop(session, test_actions_scroll_page, wheel_chain, missing):
session.execute_script("document.scrollingElement.scrollTop = 0")
outer = session.find.css("#outer", all=False)
actions = wheel_chain.scroll(0, 0, 5, 10, origin=outer)
del actions._actions[-1][missing]
with pytest.raises(InvalidArgumentException):
actions.perform()

View File

@ -1,103 +1,139 @@
<!doctype html>
<meta charset=utf-8>
<html>
<head>
<head>
<title>Test Actions</title>
<style>
div { padding: 0; margin: 0; }
.area { width: 100px; height: 50px; background-color: #ccc; }
#scrollable { width: 100px; height: 100px; overflow: scroll; }
#scrollContent { width: 600px; height: 1000px; background-color: blue; }
#subframe { width: 100px; height: 100px; }
div {
padding: 0;
margin: 0;
}
#not-scrollable {
margin-bottom: 100px;
width: 100px;
height: 50px;
}
#not-scrollable-content {
width: 200px;
height: 100px;
background-color: #ccc;
}
#scrollable {
width: 100px;
height: 100px;
overflow: scroll;
}
#scrollable-content {
width: 600px;
height: 1000px;
background-color: blue;
}
#iframe {
width: 100px;
height: 100px;
}
#event-reporter {
white-space: pre-line;
}
</style>
<script>
"use strict";
var els = {};
var allEvents = { events: [] };
function displayMessage(message) {
document.getElementById("events").innerHTML = "<p>" + message + "</p>";
}
var eventReporter;
var allEvents = { events: [] };
function appendMessage(message) {
document.getElementById("events").innerHTML += "<p>" + message + "</p>";
}
function addMessage(message) {
eventReporter.textContent = `${message}\n${eventReporter.textContent}`;
}
function recordWheelEvent(event) {
allEvents.events.push({
"type": event.type,
"button": event.button,
"buttons": event.buttons,
"deltaX": event.deltaX,
"deltaY": event.deltaY,
"deltaZ": event.deltaZ,
"deltaMode": event.deltaMode,
"target": event.target.id
});
appendMessage(event.type + " " +
"button: " + event.button + ", " +
"pageX: " + event.pageX + ", " +
"pageY: " + event.pageY + ", " +
"button: " + event.button + ", " +
"buttons: " + event.buttons + ", " +
"deltaX: " + event.deltaX + ", " +
"deltaY: " + event.deltaY + ", " +
"deltaZ: " + event.deltaZ + ", " +
"deltaMode: " + event.deltaMode + ", " +
"target id: " + event.target.id);
}
function resetEvents() {
allEvents.events.length = 0;
displayMessage("");
}
document.addEventListener("DOMContentLoaded", function() {
var outer = document.getElementById("outer");
outer.addEventListener("wheel", recordWheelEvent);
var scrollable = document.getElementById("scrollable");
scrollable.addEventListener("wheel", recordWheelEvent);
function recordWheelEvent(event) {
allEvents.events.push({
"type": event.type,
"button": event.button,
"buttons": event.buttons,
"pageX": event.pageX,
"pageY": event.pageY,
"deltaX": event.deltaX,
"deltaY": event.deltaY,
"deltaZ": event.deltaZ,
"deltaMode": event.deltaMode,
"target": event.target.id,
});
</script>
</head>
<body>
<div>
<h2>ScrollReporter</h2>
<div id="outer" class="area">
</div>
</div>
<div>
<h2>OverflowScrollReporter</h2>
<div id="scrollable">
<div id="scrollContent"></div>
</div>
</div>
<div>
<h2>IframeScrollReporter</h2>
<iframe id='subframe' srcdoc='
<script>
document.scrollingElement.addEventListener("wheel",
function(event) {
window.parent.allEvents.events.push({
"type": event.type,
"button": event.button,
"buttons": event.buttons,
"deltaX": event.deltaX,
"deltaY": event.deltaY,
"deltaZ": event.deltaZ,
"deltaMode": event.deltaMode,
"target": event.target.id
});
}
addMessage(
"type: " + event.type + " " +
"button: " + event.button + ", " +
"buttons: " + event.buttons + ", " +
"pageX: " + event.pageX + ", " +
"pageY: " + event.pageY + ", " +
"deltaX: " + event.deltaX + ", " +
"deltaY: " + event.deltaY + ", " +
"deltaZ: " + event.deltaZ + ", " +
"deltaMode: " + event.deltaMode + ", " +
"target id: " + event.target.id
);
</script>
<div id="iframeContent"
style="width: 7500px; height: 7500px; background-color:blue" ></div>'>
</iframe>
</div>
<div id="resultContainer">
<h2>Events</h2>
<div id="events"></div>
</div>
</body>
}
document.addEventListener("DOMContentLoaded", function () {
eventReporter = document.getElementById("event-reporter");
var noScroll = document.getElementById("not-scrollable");
noScroll.addEventListener("wheel", recordWheelEvent);
var scrollable = document.getElementById("scrollable");
scrollable.addEventListener("wheel", recordWheelEvent);
});
</script>
</head>
<body>
<div>
<h2>Scroll Reporter</h2>
<div id="not-scrollable">
<div id="not-scrollable-content"></div>
</div>
</div>
<div>
<h2>Overflow Scroll Reporter</h2>
<div id="scrollable">
<div id="scrollable-content"></div>
</div>
</div>
<div>
<h2>iframe Scroll Reporter</h2>
<iframe id="iframe" srcdoc='
<script>
document.scrollingElement.addEventListener("wheel", event => {
window.parent.recordWheelEvent({
"type": event.type,
"button": event.button,
"buttons": event.buttons,
"pageX": event.pageX,
"pageY": event.pageY,
"deltaX": event.deltaX,
"deltaY": event.deltaY,
"deltaZ": event.deltaZ,
"deltaMode": event.deltaMode,
"target": event.target
});
});
</script>
<div id="iframeContent" style="width: 7500px; height: 7500px; background-color:blue">
</div>'>
</iframe>
</div>
<div id="resultContainer">
<hr />
<h2>Events</h2>
<div id="event-reporter"></div>
</div>
</body>
</html>