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, ...@@ -132,6 +132,12 @@ Status StubWebView::DispatchTouchEvents(const std::list<TouchEvent>& events,
return Status(kOk); 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, Status StubWebView::DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) { bool async_dispatch_events) {
return Status(kOk); return Status(kOk);
......
...@@ -69,6 +69,9 @@ class StubWebView : public WebView { ...@@ -69,6 +69,9 @@ class StubWebView : public WebView {
bool async_dispatch_events = false) override; bool async_dispatch_events = false) override;
Status DispatchTouchEvents(const std::list<TouchEvent>& events, Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override; 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, Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events = false) override; bool async_dispatch_events = false) override;
Status GetCookies(std::unique_ptr<base::ListValue>* cookies, Status GetCookies(std::unique_ptr<base::ListValue>* cookies,
......
...@@ -153,6 +153,10 @@ class WebView { ...@@ -153,6 +153,10 @@ class WebView {
virtual Status DispatchTouchEvents(const std::list<TouchEvent>& events, virtual Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events) = 0; 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. // Dispatch a sequence of key events.
virtual Status DispatchKeyEvents(const std::list<KeyEvent>& events, virtual Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) = 0; bool async_dispatch_events) = 0;
......
...@@ -138,6 +138,19 @@ const char* GetAsString(PointerType type) { ...@@ -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 } // namespace
WebViewImpl::WebViewImpl(const std::string& id, WebViewImpl::WebViewImpl(const std::string& id,
...@@ -556,14 +569,7 @@ Status WebViewImpl::DispatchTouchEvent(const TouchEvent& event, ...@@ -556,14 +569,7 @@ Status WebViewImpl::DispatchTouchEvent(const TouchEvent& event,
std::unique_ptr<base::ListValue> point_list(new base::ListValue); std::unique_ptr<base::ListValue> point_list(new base::ListValue);
Status status(kOk); Status status(kOk);
if (type == "touchStart" || type == "touchMove") { if (type == "touchStart" || type == "touchMove") {
std::unique_ptr<base::DictionaryValue> point(new base::DictionaryValue()); std::unique_ptr<base::DictionaryValue> point = GenerateTouchPoint(event);
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);
point_list->Append(std::move(point)); point_list->Append(std::move(point));
} }
params.Set("touchPoints", std::move(point_list)); params.Set("touchPoints", std::move(point_list));
...@@ -586,6 +592,33 @@ Status WebViewImpl::DispatchTouchEvents(const std::list<TouchEvent>& events, ...@@ -586,6 +592,33 @@ Status WebViewImpl::DispatchTouchEvents(const std::list<TouchEvent>& events,
return Status(kOk); 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, Status WebViewImpl::DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events) { bool async_dispatch_events) {
Status status(kOk); Status status(kOk);
......
...@@ -109,6 +109,9 @@ class WebViewImpl : public WebView { ...@@ -109,6 +109,9 @@ class WebViewImpl : public WebView {
bool async_dispatch_events = false) override; bool async_dispatch_events = false) override;
Status DispatchTouchEvents(const std::list<TouchEvent>& events, Status DispatchTouchEvents(const std::list<TouchEvent>& events,
bool async_dispatch_events = false) override; 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, Status DispatchKeyEvents(const std::list<KeyEvent>& events,
bool async_dispatch_events = false) override; bool async_dispatch_events = false) override;
Status GetCookies(std::unique_ptr<base::ListValue>* cookies, Status GetCookies(std::unique_ptr<base::ListValue>* cookies,
......
...@@ -948,6 +948,43 @@ class ChromeDriverTest(ChromeDriverBaseTestWithWebServer): ...@@ -948,6 +948,43 @@ class ChromeDriverTest(ChromeDriverBaseTestWithWebServer):
self._driver.PerformActions(actions) self._driver.PerformActions(actions)
self.assertEquals(1, len(self._driver.FindElements('tag name', 'br'))) 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): def testActionsMulti(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html')) self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
self._driver.ExecuteScript( self._driver.ExecuteScript(
......
...@@ -1288,7 +1288,8 @@ Status ExecutePerformActions(Session* session, ...@@ -1288,7 +1288,8 @@ Status ExecutePerformActions(Session* session,
for (size_t i = 0; i < longest_action_list_size; i++) { for (size_t i = 0; i < longest_action_list_size; i++) {
// Find the last pointer action, and it has to be sent synchronously by // Find the last pointer action, and it has to be sent synchronously by
// default. // 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++) { for (size_t j = 0; j < actions_list.size(); j++) {
if (actions_list[j].size() > i) { if (actions_list[j].size() > i) {
const base::DictionaryValue* action = actions_list[j][i].get(); const base::DictionaryValue* action = actions_list[j][i].get();
...@@ -1296,8 +1297,14 @@ Status ExecutePerformActions(Session* session, ...@@ -1296,8 +1297,14 @@ Status ExecutePerformActions(Session* session,
std::string action_type; std::string action_type;
action->GetString("type", &type); action->GetString("type", &type);
action->GetString("subtype", &action_type); action->GetString("subtype", &action_type);
if (type != "none" && action_type != "pause") { if (type != "none" && action_type != "pause")
last_action = j; 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, ...@@ -1306,6 +1313,7 @@ Status ExecutePerformActions(Session* session,
// (https://w3c.github.io/webdriver/#dfn-computing-the-tick-duration). // (https://w3c.github.io/webdriver/#dfn-computing-the-tick-duration).
// This is the duration for actions in one tick. // This is the duration for actions in one tick.
int tick_duration = 0; int tick_duration = 0;
std::list<TouchEvent> dispatch_touch_events;
for (size_t j = 0; j < actions_list.size(); j++) { for (size_t j = 0; j < actions_list.size(); j++) {
if (actions_list[j].size() > i) { if (actions_list[j].size() > i) {
const base::DictionaryValue* action = actions_list[j][i].get(); const base::DictionaryValue* action = actions_list[j][i].get();
...@@ -1351,7 +1359,7 @@ Status ExecutePerformActions(Session* session, ...@@ -1351,7 +1359,7 @@ Status ExecutePerformActions(Session* session,
tick_duration = std::max(tick_duration, duration); tick_duration = std::max(tick_duration, duration);
} else { } else {
bool async_dispatch_event = true; bool async_dispatch_event = true;
if (j == last_action) { if (j == last_action_index) {
async_dispatch_event = false; async_dispatch_event = false;
GetOptionalBool(action, "asyncDispatch", &async_dispatch_event); GetOptionalBool(action, "asyncDispatch", &async_dispatch_event);
} }
...@@ -1486,10 +1494,14 @@ Status ExecutePerformActions(Session* session, ...@@ -1486,10 +1494,14 @@ Status ExecutePerformActions(Session* session,
action_input_states[j]->SetInteger("pressed", 0); action_input_states[j]->SetInteger("pressed", 0);
} }
if (has_touch_start[id]) { if (has_touch_start[id]) {
Status status = event.id = dispatch_touch_events.size();
web_view->DispatchTouchEvent(event, async_dispatch_event); dispatch_touch_events.push_back(event);
if (status.IsError()) if (j == last_touch_index) {
return status; Status status = web_view->DispatchTouchEventWithMultiPoints(
dispatch_touch_events, async_dispatch_event);
if (status.IsError())
return status;
}
} }
if (action_type == "pointerUp") if (action_type == "pointerUp")
has_touch_start[id] = false; 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