Commit bc67b125 authored by Lan Wei's avatar Lan Wei Committed by Commit Bot

[Chromedriver] Support multiple touch fingers in PerformAction

Right now, we are dispatch touch events one touch point by one touch
point to Devtool input protocol, but Devtool does not group them
together as one touch event. Therefore, when we send touch events to
Devtool we need to group all the touch points into one touch event and
then send to Devtool.

Bug: 999982
Change-Id: I11a85625ede0651b920d732a4e1006292f8be0a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1838138Reviewed-by: default avatarJohn Chen <johnchen@chromium.org>
Commit-Queue: Lan Wei <lanwei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#702612}
parent b8403fda
......@@ -132,6 +132,12 @@ Status StubWebView::DispatchTouchEvents(const std::list<TouchEvent>& events,
return Status(kOk);
}
Status StubWebView::DispatchTouchEventWithMultiPoints(
const std::list<TouchEvent>& events,
bool async_dispatch_events) {
return Status(kOk);
}
Status StubWebView::DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) {
return Status(kOk);
......
......@@ -69,6 +69,9 @@ class StubWebView : public WebView {
bool async_dispatch_events = false) override;
Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override;
Status DispatchTouchEventWithMultiPoints(
const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override;
Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events = false) override;
Status GetCookies(std::unique_ptr<base::ListValue>* cookies,
......
......@@ -153,6 +153,10 @@ class WebView {
virtual Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events) = 0;
// Dispatch a single touch event with more than one touch point.
virtual Status DispatchTouchEventWithMultiPoints(
const std::list<TouchEvent>& events,
bool async_dispatch_events) = 0;
// Dispatch a sequence of key events.
virtual Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) = 0;
......
......@@ -138,6 +138,19 @@ const char* GetAsString(PointerType type) {
}
}
std::unique_ptr<base::DictionaryValue> GenerateTouchPoint(
const TouchEvent& event) {
std::unique_ptr<base::DictionaryValue> point(new base::DictionaryValue());
point->SetInteger("x", event.x);
point->SetInteger("y", event.y);
point->SetDouble("radiusX", event.radiusX);
point->SetDouble("radiusY", event.radiusY);
point->SetDouble("rotationAngle", event.rotationAngle);
point->SetDouble("force", event.force);
point->SetInteger("id", event.id);
return point;
}
} // namespace
WebViewImpl::WebViewImpl(const std::string& id,
......@@ -556,14 +569,7 @@ Status WebViewImpl::DispatchTouchEvent(const TouchEvent& event,
std::unique_ptr<base::ListValue> point_list(new base::ListValue);
Status status(kOk);
if (type == "touchStart" || type == "touchMove") {
std::unique_ptr<base::DictionaryValue> point(new base::DictionaryValue());
point->SetInteger("x", event.x);
point->SetInteger("y", event.y);
point->SetDouble("radiusX", event.radiusX);
point->SetDouble("radiusY", event.radiusY);
point->SetDouble("rotationAngle", event.rotationAngle);
point->SetDouble("force", event.force);
point->SetInteger("id", event.id);
std::unique_ptr<base::DictionaryValue> point = GenerateTouchPoint(event);
point_list->Append(std::move(point));
}
params.Set("touchPoints", std::move(point_list));
......@@ -586,6 +592,33 @@ Status WebViewImpl::DispatchTouchEvents(const std::list<TouchEvent>& events,
return Status(kOk);
}
Status WebViewImpl::DispatchTouchEventWithMultiPoints(
const std::list<TouchEvent>& events,
bool async_dispatch_events) {
if (events.size() == 0)
return Status(kOk);
base::DictionaryValue params;
std::string type = GetAsString(events.front().type);
params.SetString("type", type);
std::unique_ptr<base::ListValue> point_list(new base::ListValue);
Status status(kOk);
for (auto it = events.begin(); it != events.end(); ++it) {
if (type == "touchStart" || type == "touchMove") {
std::unique_ptr<base::DictionaryValue> point = GenerateTouchPoint(*it);
point_list->Append(std::move(point));
}
}
params.Set("touchPoints", std::move(point_list));
if (async_dispatch_events) {
status = client_->SendCommandAndIgnoreResponse("Input.dispatchTouchEvent",
params);
} else {
status = client_->SendCommand("Input.dispatchTouchEvent", params);
}
return Status(kOk);
}
Status WebViewImpl::DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) {
Status status(kOk);
......
......@@ -109,6 +109,9 @@ class WebViewImpl : public WebView {
bool async_dispatch_events = false) override;
Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override;
Status DispatchTouchEventWithMultiPoints(
const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override;
Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events = false) override;
Status GetCookies(std::unique_ptr<base::ListValue>* cookies,
......
......@@ -948,6 +948,43 @@ class ChromeDriverTest(ChromeDriverBaseTestWithWebServer):
self._driver.PerformActions(actions)
self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
def testActionsMultiTouchPoint(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
self._driver.ExecuteScript(
'document.body.innerHTML = "<div>old</div>";'
'var div = document.getElementsByTagName("div")[0];'
'window.events = [];'
'var touch_point_count = 0;'
'div.style["width"] = "100px";'
'div.style["height"] = "100px";'
'div.addEventListener("pointerdown", function() {'
' touch_point_count++;'
' window.events.push('
' {x: event.clientX, y: event.clientY});'
'});'
'return div;')
actions = ({"actions": [{
"type":"pointer",
"actions":[{"type": "pointerMove", "x": 10, "y": 10},
{"type": "pointerDown"},
{"type": "pointerUp"}],
"parameters": {"pointerType": "touch"},
"id": "pointer1"},
{
"type":"pointer",
"actions":[{"type": "pointerMove", "x": 15, "y": 15},
{"type": "pointerDown"},
{"type": "pointerUp"}],
"parameters": {"pointerType": "touch"},
"id": "pointer2"}]})
self._driver.PerformActions(actions)
events = self._driver.ExecuteScript('return window.events')
self.assertEquals(2, len(events))
self.assertEquals(10, events[0]['x'])
self.assertEquals(10, events[0]['y'])
self.assertEquals(15, events[1]['x'])
self.assertEquals(15, events[1]['y'])
def testActionsMulti(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
self._driver.ExecuteScript(
......
......@@ -1288,7 +1288,8 @@ Status ExecutePerformActions(Session* session,
for (size_t i = 0; i < longest_action_list_size; i++) {
// Find the last pointer action, and it has to be sent synchronously by
// default.
size_t last_action = 0;
size_t last_action_index = 0;
size_t last_touch_index = 0;
for (size_t j = 0; j < actions_list.size(); j++) {
if (actions_list[j].size() > i) {
const base::DictionaryValue* action = actions_list[j][i].get();
......@@ -1296,8 +1297,14 @@ Status ExecutePerformActions(Session* session,
std::string action_type;
action->GetString("type", &type);
action->GetString("subtype", &action_type);
if (type != "none" && action_type != "pause") {
last_action = j;
if (type != "none" && action_type != "pause")
last_action_index = j;
if (type == "pointer") {
std::string pointer_type;
action->GetString("pointerType", &pointer_type);
if (pointer_type == "touch")
last_touch_index = j;
}
}
}
......@@ -1306,6 +1313,7 @@ Status ExecutePerformActions(Session* session,
// (https://w3c.github.io/webdriver/#dfn-computing-the-tick-duration).
// This is the duration for actions in one tick.
int tick_duration = 0;
std::list<TouchEvent> dispatch_touch_events;
for (size_t j = 0; j < actions_list.size(); j++) {
if (actions_list[j].size() > i) {
const base::DictionaryValue* action = actions_list[j][i].get();
......@@ -1351,7 +1359,7 @@ Status ExecutePerformActions(Session* session,
tick_duration = std::max(tick_duration, duration);
} else {
bool async_dispatch_event = true;
if (j == last_action) {
if (j == last_action_index) {
async_dispatch_event = false;
GetOptionalBool(action, "asyncDispatch", &async_dispatch_event);
}
......@@ -1486,11 +1494,15 @@ Status ExecutePerformActions(Session* session,
action_input_states[j]->SetInteger("pressed", 0);
}
if (has_touch_start[id]) {
Status status =
web_view->DispatchTouchEvent(event, async_dispatch_event);
event.id = dispatch_touch_events.size();
dispatch_touch_events.push_back(event);
if (j == last_touch_index) {
Status status = web_view->DispatchTouchEventWithMultiPoints(
dispatch_touch_events, async_dispatch_event);
if (status.IsError())
return status;
}
}
if (action_type == "pointerUp")
has_touch_start[id] = false;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment