summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/test/chromedriver/chrome/stub_web_view.cc18
-rw-r--r--chrome/test/chromedriver/chrome/stub_web_view.h9
-rw-r--r--chrome/test/chromedriver/chrome/web_view.h12
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.cc33
-rw-r--r--chrome/test/chromedriver/chrome/web_view_impl.h9
-rw-r--r--chrome/test/chromedriver/client/chromedriver.py8
-rw-r--r--chrome/test/chromedriver/client/command_executor.py1
-rw-r--r--chrome/test/chromedriver/client/webelement.py6
-rw-r--r--chrome/test/chromedriver/element_commands.cc52
-rw-r--r--chrome/test/chromedriver/element_commands.h16
-rw-r--r--chrome/test/chromedriver/server/http_handler.cc13
-rwxr-xr-xchrome/test/chromedriver/test/run_py_tests.py224
-rw-r--r--chrome/test/chromedriver/window_commands.cc48
-rw-r--r--chrome/test/chromedriver/window_commands.h13
-rw-r--r--chrome/test/data/chromedriver/touch_action_tests.html31
-rw-r--r--content/browser/devtools/protocol/devtools_protocol_browsertest.cc134
-rw-r--r--content/browser/devtools/protocol/input_handler.cc182
-rw-r--r--content/browser/devtools/protocol/input_handler.h28
-rw-r--r--content/browser/devtools/render_frame_devtools_agent_host.cc4
-rw-r--r--content/test/data/devtools/synthetic_gesture_tests.html21
20 files changed, 743 insertions, 119 deletions
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.cc b/chrome/test/chromedriver/chrome/stub_web_view.cc
index 12c6eee..57df23f 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.cc
+++ b/chrome/test/chromedriver/chrome/stub_web_view.cc
@@ -148,3 +148,21 @@ Status StubWebView::StartProfile() {
Status StubWebView::EndProfile(scoped_ptr<base::Value>* profile_data) {
return Status(kOk);
}
+
+Status StubWebView::SynthesizeTapGesture(int x,
+ int y,
+ int tap_count,
+ bool is_long_press) {
+ return Status(kOk);
+}
+
+Status StubWebView::SynthesizeScrollGesture(int x,
+ int y,
+ int xoffset,
+ int yoffset) {
+ return Status(kOk);
+}
+
+Status StubWebView::SynthesizePinchGesture(int x, int y, double scale_factor) {
+ return Status(kOk);
+}
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.h b/chrome/test/chromedriver/chrome/stub_web_view.h
index 4b23879..1d85eb6 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.h
+++ b/chrome/test/chromedriver/chrome/stub_web_view.h
@@ -69,6 +69,15 @@ class StubWebView : public WebView {
Status TakeHeapSnapshot(scoped_ptr<base::Value>* snapshot) override;
Status StartProfile() override;
Status EndProfile(scoped_ptr<base::Value>* profile_data) override;
+ Status SynthesizeTapGesture(int x,
+ int y,
+ int tap_count,
+ bool is_long_press) override;
+ Status SynthesizeScrollGesture(int x,
+ int y,
+ int xoffset,
+ int yoffset) override;
+ Status SynthesizePinchGesture(int x, int y, double scale_factor) override;
private:
std::string id_;
diff --git a/chrome/test/chromedriver/chrome/web_view.h b/chrome/test/chromedriver/chrome/web_view.h
index 47c0cca..f184e92 100644
--- a/chrome/test/chromedriver/chrome/web_view.h
+++ b/chrome/test/chromedriver/chrome/web_view.h
@@ -172,6 +172,18 @@ class WebView {
// CPUProfile objects. The format for the captured profile is defined
// (by DevTools) in protocol.json.
virtual Status EndProfile(scoped_ptr<base::Value>* profile_data) = 0;
+
+ virtual Status SynthesizeTapGesture(int x,
+ int y,
+ int tap_count,
+ bool is_long_press) = 0;
+
+ virtual Status SynthesizeScrollGesture(int x,
+ int y,
+ int xoffset,
+ int yoffset) = 0;
+
+ virtual Status SynthesizePinchGesture(int x, int y, double scale_factor) = 0;
};
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_WEB_VIEW_H_
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 712a36c..16e3df1 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -540,6 +540,39 @@ Status WebViewImpl::EndProfile(scoped_ptr<base::Value>* profile_data) {
return status;
}
+Status WebViewImpl::SynthesizeTapGesture(int x,
+ int y,
+ int tap_count,
+ bool is_long_press) {
+ base::DictionaryValue params;
+ params.SetInteger("x", x);
+ params.SetInteger("y", y);
+ params.SetInteger("tapCount", tap_count);
+ if (is_long_press)
+ params.SetInteger("duration", 1500);
+ return client_->SendCommand("Input.synthesizeTapGesture", params);
+}
+
+Status WebViewImpl::SynthesizeScrollGesture(int x,
+ int y,
+ int xoffset,
+ int yoffset) {
+ base::DictionaryValue params;
+ params.SetInteger("x", x);
+ params.SetInteger("y", y);
+ params.SetInteger("xdistance", xoffset);
+ params.SetInteger("ydistance", yoffset);
+ return client_->SendCommand("Input.synthesizeScrollGesture", params);
+}
+
+Status WebViewImpl::SynthesizePinchGesture(int x, int y, double scale_factor) {
+ base::DictionaryValue params;
+ params.SetInteger("x", x);
+ params.SetInteger("y", y);
+ params.SetDouble("scaleFactor", scale_factor);
+ return client_->SendCommand("Input.synthesizePinchGesture", params);
+}
+
Status WebViewImpl::CallAsyncFunctionInternal(const std::string& frame,
const std::string& function,
const base::ListValue& args,
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index 9fcadaf..dd04538 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -97,6 +97,15 @@ class WebViewImpl : public WebView {
Status TakeHeapSnapshot(scoped_ptr<base::Value>* snapshot) override;
Status StartProfile() override;
Status EndProfile(scoped_ptr<base::Value>* profile_data) override;
+ Status SynthesizeTapGesture(int x,
+ int y,
+ int tap_count,
+ bool is_long_press) override;
+ Status SynthesizeScrollGesture(int x,
+ int y,
+ int xoffset,
+ int yoffset) override;
+ Status SynthesizePinchGesture(int x, int y, double scale_factor) override;
private:
Status TraverseHistoryWithJavaScript(int delta);
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index 53dcf49..e084e57 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -281,6 +281,10 @@ class ChromeDriver(object):
def TouchMove(self, x, y):
self.ExecuteCommand(Command.TOUCH_MOVE, {'x': x, 'y': y})
+ def TouchScroll(self, element, xoffset, yoffset):
+ params = {'element': element._id, 'xoffset': xoffset, 'yoffset': yoffset}
+ self.ExecuteCommand(Command.TOUCH_SCROLL, params)
+
def TouchFlick(self, element, xoffset, yoffset, speed):
params = {
'element': element._id,
@@ -290,6 +294,10 @@ class ChromeDriver(object):
}
self.ExecuteCommand(Command.TOUCH_FLICK, params)
+ def TouchPinch(self, x, y, scale):
+ params = {'x': x, 'y': y, 'scale': scale}
+ self.ExecuteCommand(Command.TOUCH_PINCH, params)
+
def GetCookies(self):
return self.ExecuteCommand(Command.GET_COOKIES)
diff --git a/chrome/test/chromedriver/client/command_executor.py b/chrome/test/chromedriver/client/command_executor.py
index fd4a464..4d3c314 100644
--- a/chrome/test/chromedriver/client/command_executor.py
+++ b/chrome/test/chromedriver/client/command_executor.py
@@ -149,6 +149,7 @@ class Command(object):
# Custom Chrome commands.
IS_LOADING = (_Method.GET, '/session/:sessionId/is_loading')
+ TOUCH_PINCH = (_Method.POST, '/session/:sessionId/touch/pinch')
class CommandExecutor(object):
diff --git a/chrome/test/chromedriver/client/webelement.py b/chrome/test/chromedriver/client/webelement.py
index ebfbc7e..64d3b31 100644
--- a/chrome/test/chromedriver/client/webelement.py
+++ b/chrome/test/chromedriver/client/webelement.py
@@ -37,6 +37,12 @@ class WebElement(object):
def SingleTap(self):
self._Execute(Command.TOUCH_SINGLE_TAP)
+ def DoubleTap(self):
+ self._Execute(Command.TOUCH_DOUBLE_TAP)
+
+ def LongPress(self):
+ self._Execute(Command.TOUCH_LONG_PRESS)
+
def Clear(self):
self._Execute(Command.CLEAR_ELEMENT)
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index 0dcf7b3..b414cf7 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -16,6 +16,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/test/chromedriver/basic_types.h"
+#include "chrome/test/chromedriver/chrome/browser_info.h"
#include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/js.h"
#include "chrome/test/chromedriver/chrome/status.h"
@@ -191,13 +192,52 @@ Status ExecuteTouchSingleTap(
session, web_view, element_id, &location);
if (status.IsError())
return status;
+ if (session->chrome->GetBrowserInfo()->build_no < 2388) {
+ // TODO(samuong): remove this once we stop supporting M44.
+ std::list<TouchEvent> events;
+ events.push_back(
+ TouchEvent(kTouchStart, location.x, location.y));
+ events.push_back(
+ TouchEvent(kTouchEnd, location.x, location.y));
+ return web_view->DispatchTouchEvents(events);
+ }
+ return web_view->SynthesizeTapGesture(location.x, location.y, 1, false);
+}
- std::list<TouchEvent> events;
- events.push_back(
- TouchEvent(kTouchStart, location.x, location.y));
- events.push_back(
- TouchEvent(kTouchEnd, location.x, location.y));
- return web_view->DispatchTouchEvents(events);
+Status ExecuteTouchDoubleTap(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ if (session->chrome->GetBrowserInfo()->build_no < 2388) {
+ // TODO(samuong): remove this once we stop supporting M44.
+ return Status(kUnknownCommand, "Double tap command requires Chrome 44+");
+ }
+ WebPoint location;
+ Status status = GetElementClickableLocation(
+ session, web_view, element_id, &location);
+ if (status.IsError())
+ return status;
+ return web_view->SynthesizeTapGesture(location.x, location.y, 2, false);
+}
+
+Status ExecuteTouchLongPress(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ if (session->chrome->GetBrowserInfo()->build_no < 2388) {
+ // TODO(samuong): remove this once we stop supporting M44.
+ return Status(kUnknownCommand, "Long press command requires Chrome 44+");
+ }
+ WebPoint location;
+ Status status = GetElementClickableLocation(
+ session, web_view, element_id, &location);
+ if (status.IsError())
+ return status;
+ return web_view->SynthesizeTapGesture(location.x, location.y, 1, true);
}
Status ExecuteFlick(
diff --git a/chrome/test/chromedriver/element_commands.h b/chrome/test/chromedriver/element_commands.h
index 6d94bf3..146f28a 100644
--- a/chrome/test/chromedriver/element_commands.h
+++ b/chrome/test/chromedriver/element_commands.h
@@ -76,6 +76,22 @@ Status ExecuteTouchSingleTap(
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
+// Double tap on the element.
+Status ExecuteTouchDoubleTap(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
+// Long press on the element.
+Status ExecuteTouchLongPress(
+ Session* session,
+ WebView* web_view,
+ const std::string& element_id,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
// Touch flick starting on the element.
Status ExecuteFlick(
Session* session,
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 65f05ee..5296a93 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -512,13 +512,16 @@ HttpHandler::HttpHandler(
WrapToCommand("TouchMove", base::Bind(&ExecuteTouchMove))),
CommandMapping(kPost,
"session/:sessionId/touch/scroll",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand("TouchScroll",
+ base::Bind(&ExecuteTouchScroll))),
CommandMapping(kPost,
"session/:sessionId/touch/doubleclick",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand("TouchDoubleTap",
+ base::Bind(&ExecuteTouchDoubleTap))),
CommandMapping(kPost,
"session/:sessionId/touch/longclick",
- base::Bind(&UnimplementedCommand)),
+ WrapToCommand("TouchLongPress",
+ base::Bind(&ExecuteTouchLongPress))),
CommandMapping(kPost,
"session/:sessionId/touch/flick",
WrapToCommand("TouchFlick", base::Bind(&ExecuteFlick))),
@@ -558,6 +561,10 @@ HttpHandler::HttpHandler(
WrapToCommand(
"SetAutoReporting",
base::Bind(&ExecuteSetAutoReporting))),
+ CommandMapping(kPost,
+ "session/:sessionId/touch/pinch",
+ WrapToCommand("TouchPinch",
+ base::Bind(&ExecuteTouchPinch))),
};
command_map_.reset(
new CommandMap(commands, commands + arraysize(commands)));
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index ef0506d..3b2b6c5 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -54,6 +54,8 @@ _NEGATIVE_FILTER = [
# on developer workstations.
'ChromeDriverTest.testEmulateNetworkConditionsNameSpeed',
'ChromeDriverTest.testEmulateNetworkConditionsSpeed',
+ # crbug.com/469947
+ 'ChromeDriverTest.testTouchPinch'
]
_VERSION_SPECIFIC_FILTER = {}
@@ -93,10 +95,13 @@ _OS_SPECIFIC_FILTER['mac'] = [
_DESKTOP_NEGATIVE_FILTER = [
# Desktop doesn't support touch (without --touch-events).
- 'ChromeDriverTest.testSingleTapElement',
- 'ChromeDriverTest.testTouchDownUpElement',
+ 'ChromeDriverTest.testTouchSingleTapElement',
+ 'ChromeDriverTest.testTouchDownMoveUpElement',
+ 'ChromeDriverTest.testTouchScrollElement',
+ 'ChromeDriverTest.testTouchDoubleTapElement',
+ 'ChromeDriverTest.testTouchLongPressElement',
'ChromeDriverTest.testTouchFlickElement',
- 'ChromeDriverTest.testTouchMovedElement',
+ 'ChromeDriverTest.testTouchPinch',
'ChromeDriverAndroidTest.*',
]
@@ -141,9 +146,25 @@ _ANDROID_NEGATIVE_FILTER['chrome'] = (
]
)
_ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
- _ANDROID_NEGATIVE_FILTER['chrome'])
+ _ANDROID_NEGATIVE_FILTER['chrome'] + [
+ # The stable channel Chrome for Android does not yet support Synthetic
+ # Gesture DevTools commands.
+ # TODO(samuong): reenable when it does.
+ 'ChromeDriverTest.testTouchScrollElement',
+ 'ChromeDriverTest.testTouchDoubleTapElement',
+ 'ChromeDriverTest.testTouchLongPressElement',
+ 'ChromeDriverTest.testTouchPinch',
+ ])
_ANDROID_NEGATIVE_FILTER['chrome_beta'] = (
- _ANDROID_NEGATIVE_FILTER['chrome'])
+ _ANDROID_NEGATIVE_FILTER['chrome'] + [
+ # The beta channel Chrome for Android does not yet support Synthetic
+ # Gesture DevTools commands.
+ # TODO(samuong): reenable when it does.
+ 'ChromeDriverTest.testTouchScrollElement',
+ 'ChromeDriverTest.testTouchDoubleTapElement',
+ 'ChromeDriverTest.testTouchLongPressElement',
+ 'ChromeDriverTest.testTouchPinch',
+ ])
_ANDROID_NEGATIVE_FILTER['chrome_shell'] = (
_ANDROID_NEGATIVE_FILTER['chrome'] + [
# ChromeShell doesn't support multiple tabs.
@@ -447,72 +468,6 @@ class ChromeDriverTest(ChromeDriverBaseTest):
div.Click()
self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
- def testSingleTapElement(self):
- div = self._driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchend", function() {'
- ' div.innerHTML="new<br>";'
- '});'
- 'return div;')
- div.SingleTap()
- self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
-
- def testTouchDownUpElement(self):
- div = self._driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchend", function() {'
- ' div.innerHTML="new<br>";'
- '});'
- 'return div;')
- loc = div.GetLocation()
- self._driver.TouchDown(loc['x'], loc['y'])
- self._driver.TouchUp(loc['x'], loc['y'])
- self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
-
- def testTouchFlickElement(self):
- dx = 3
- dy = 4
- speed = 5
- flickTouchEventsPerSecond = 30
- moveEvents = int(
- math.sqrt(dx * dx + dy * dy) * flickTouchEventsPerSecond / speed)
- div = self._driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchstart", function() {'
- ' div.innerHTML = "preMove0";'
- '});'
- 'div.addEventListener("touchmove", function() {'
- ' res = div.innerHTML.match(/preMove(\d+)/);'
- ' if (res != null) {'
- ' div.innerHTML = "preMove" + (parseInt(res[1], 10) + 1);'
- ' }'
- '});'
- 'div.addEventListener("touchend", function() {'
- ' if (div.innerHTML == "preMove' + str(moveEvents) + '") {'
- ' div.innerHTML = "new<br>";'
- ' }'
- '});'
- 'return div;')
- self._driver.TouchFlick(div, dx, dy, speed)
- self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
-
- def testTouchMovedElement(self):
- div = self._driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchmove", function() {'
- ' div.innerHTML="new<br>";'
- '});'
- 'return div;')
- loc = div.GetLocation()
- self._driver.TouchDown(loc['x'], loc['y'])
- self._driver.TouchMove(loc['x'] + 1, loc['y'] + 1)
- self._driver.TouchUp(loc['x'] + 1, loc['y'] + 1)
- self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
-
def testClickElementInSubFrame(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/frame_test.html'))
frame = self._driver.FindElement('tag name', 'iframe')
@@ -988,6 +943,104 @@ class ChromeDriverTest(ChromeDriverBaseTest):
'document.querySelector("#outerDiv").style.display="None";')
self.assertFalse(elem.IsDisplayed())
+ def testTouchSingleTapElement(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ events = self._driver.FindElement('id', 'events')
+ events.SingleTap()
+ self.assertEquals('events: touchstart touchend', events.GetText())
+
+ def testTouchDownMoveUpElement(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ events = self._driver.FindElement('id', 'events')
+ location = events.GetLocation()
+ self._driver.TouchDown(location['x'], location['y'])
+ self.assertEquals('events: touchstart', events.GetText())
+ self._driver.TouchMove(location['x'] + 1, location['y'] + 1)
+ self.assertEquals('events: touchstart touchmove', events.GetText())
+ self._driver.TouchUp(location['x'] + 1, location['y'] + 1)
+ self.assertEquals('events: touchstart touchmove touchend', events.GetText())
+
+ def testTouchScrollElement(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ events = self._driver.FindElement('id', 'events')
+ self.assertTrue(events.IsDisplayed())
+ xoffset = 0
+ yoffset = self._driver.ExecuteScript('return window.screen.height * 2;')
+ self._driver.TouchScroll(events, xoffset, yoffset)
+ bottom = self._driver.FindElement('id', 'bottom')
+ self.assertTrue(bottom.IsDisplayed())
+
+ def testTouchDoubleTapElement(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ events = self._driver.FindElement('id', 'events')
+ events.DoubleTap()
+ self.assertEquals('events: touchstart touchend touchstart touchend',
+ events.GetText())
+
+ def testTouchLongPressElement(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ events = self._driver.FindElement('id', 'events')
+ events.LongPress()
+ self.assertEquals('events: touchstart touchcancel', events.GetText())
+
+ def testTouchFlickElement(self):
+ dx = 3
+ dy = 4
+ speed = 5
+ flickTouchEventsPerSecond = 30
+ moveEvents = int(
+ math.sqrt(dx * dx + dy * dy) * flickTouchEventsPerSecond / speed)
+ div = self._driver.ExecuteScript(
+ 'document.body.innerHTML = "<div>old</div>";'
+ 'var div = document.getElementsByTagName("div")[0];'
+ 'div.addEventListener("touchstart", function() {'
+ ' div.innerHTML = "preMove0";'
+ '});'
+ 'div.addEventListener("touchmove", function() {'
+ ' res = div.innerHTML.match(/preMove(\d+)/);'
+ ' if (res != null) {'
+ ' div.innerHTML = "preMove" + (parseInt(res[1], 10) + 1);'
+ ' }'
+ '});'
+ 'div.addEventListener("touchend", function() {'
+ ' if (div.innerHTML == "preMove' + str(moveEvents) + '") {'
+ ' div.innerHTML = "new<br>";'
+ ' }'
+ '});'
+ 'return div;')
+ self._driver.TouchFlick(div, dx, dy, speed)
+ self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
+
+ def testTouchPinch(self):
+ self._driver.Load(self.GetHttpUrlForFile(
+ '/chromedriver/touch_action_tests.html'))
+ width_before_pinch = self._driver.ExecuteScript('return window.innerWidth;')
+ height_before_pinch = self._driver.ExecuteScript(
+ 'return window.innerHeight;')
+ self._driver.TouchPinch(width_before_pinch / 2,
+ height_before_pinch / 2,
+ 2.0)
+ width_after_pinch = self._driver.ExecuteScript('return window.innerWidth;')
+ self.assertAlmostEqual(2.0, float(width_before_pinch) / width_after_pinch)
+
+ def testBrowserDoesntSupportSyntheticGestures(self):
+ # Current versions of stable and beta channel Chrome for Android do not
+ # support synthetic gesture commands in DevTools, so touch action tests have
+ # been disabled for chrome_stable and chrome_beta.
+ # TODO(samuong): when this test starts failing, re-enable touch tests and
+ # delete this test.
+ if _ANDROID_PACKAGE_KEY:
+ if _ANDROID_PACKAGE_KEY in ['chrome_stable', 'chrome_beta']:
+ self.assertRaisesRegexp(RuntimeError,
+ 'Server returned error: Not Implemented',
+ self._driver.TouchPinch, 1, 2, 3.0)
+
+
class ChromeDriverAndroidTest(ChromeDriverBaseTest):
"""End to end tests for Android-specific tests."""
@@ -1279,35 +1332,6 @@ class MobileEmulationCapabilityTest(ChromeDriverBaseTest):
div.Click()
self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
- def testSingleTapElement(self):
- driver = self.CreateDriver(
- mobile_emulation = {'deviceName': 'Google Nexus 5'})
- driver.Load('about:blank')
- div = driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchend", function() {'
- ' div.innerHTML="new<br>";'
- '});'
- 'return div;')
- div.SingleTap()
- self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
-
- def testTouchDownUpElement(self):
- driver = self.CreateDriver(
- mobile_emulation = {'deviceName': 'Google Nexus 5'})
- div = driver.ExecuteScript(
- 'document.body.innerHTML = "<div>old</div>";'
- 'var div = document.getElementsByTagName("div")[0];'
- 'div.addEventListener("touchend", function() {'
- ' div.innerHTML="new<br>";'
- '});'
- 'return div;')
- loc = div.GetLocation()
- driver.TouchDown(loc['x'], loc['y'])
- driver.TouchUp(loc['x'], loc['y'])
- self.assertEquals(1, len(driver.FindElements('tag name', 'br')))
-
class ChromeDriverLogTest(unittest.TestCase):
"""Tests that chromedriver produces the expected log file."""
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index 075f8b1..7c5c0ec 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -15,6 +15,7 @@
#include "base/values.h"
#include "chrome/test/chromedriver/basic_types.h"
#include "chrome/test/chromedriver/chrome/automation_extension.h"
+#include "chrome/test/chromedriver/chrome/browser_info.h"
#include "chrome/test/chromedriver/chrome/chrome.h"
#include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
#include "chrome/test/chromedriver/chrome/devtools_client.h"
@@ -593,6 +594,53 @@ Status ExecuteTouchMove(
return ExecuteTouchEvent(session, web_view, kTouchMove, params);
}
+Status ExecuteTouchScroll(
+ Session *session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ if (session->chrome->GetBrowserInfo()->build_no < 2286) {
+ // TODO(samuong): remove this once we stop supporting M41.
+ return Status(kUnknownCommand, "Touch scroll action requires Chrome 42+");
+ }
+ WebPoint location = session->mouse_position;
+ std::string element;
+ if (params.GetString("element", &element)) {
+ Status status = GetElementClickableLocation(
+ session, web_view, element, &location);
+ if (status.IsError())
+ return status;
+ }
+ int xoffset;
+ if (!params.GetInteger("xoffset", &xoffset))
+ return Status(kUnknownError, "'xoffset' must be an integer");
+ int yoffset;
+ if (!params.GetInteger("yoffset", &yoffset))
+ return Status(kUnknownError, "'yoffset' must be an integer");
+ return web_view->SynthesizeScrollGesture(
+ location.x, location.y, xoffset, yoffset);
+}
+
+Status ExecuteTouchPinch(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value) {
+ if (session->chrome->GetBrowserInfo()->build_no < 2286) {
+ // TODO(samuong): remove this once we stop supporting M41.
+ return Status(kUnknownCommand, "Pinch action requires Chrome 42+");
+ }
+ WebPoint location;
+ if (!params.GetInteger("x", &location.x))
+ return Status(kUnknownError, "'x' must be an integer");
+ if (!params.GetInteger("y", &location.y))
+ return Status(kUnknownError, "'y' must be an integer");
+ double scale_factor;
+ if (!params.GetDouble("scale", &scale_factor))
+ return Status(kUnknownError, "'scale' must be an integer");
+ return web_view->SynthesizePinchGesture(location.x, location.y, scale_factor);
+}
+
Status ExecuteGetActiveElement(
Session* session,
WebView* web_view,
diff --git a/chrome/test/chromedriver/window_commands.h b/chrome/test/chromedriver/window_commands.h
index ec4a68c..fa36e2b 100644
--- a/chrome/test/chromedriver/window_commands.h
+++ b/chrome/test/chromedriver/window_commands.h
@@ -181,6 +181,19 @@ Status ExecuteTouchMove(
const base::DictionaryValue& params,
scoped_ptr<base::Value>* value);
+// Do a swipe (scroll) gesture beginning at the element.
+Status ExecuteTouchScroll(
+ Session *session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
+Status ExecuteTouchPinch(
+ Session* session,
+ WebView* web_view,
+ const base::DictionaryValue& params,
+ scoped_ptr<base::Value>* value);
+
Status ExecuteGetActiveElement(
Session* session,
WebView* web_view,
diff --git a/chrome/test/data/chromedriver/touch_action_tests.html b/chrome/test/data/chromedriver/touch_action_tests.html
new file mode 100644
index 0000000..9a1e15d
--- /dev/null
+++ b/chrome/test/data/chromedriver/touch_action_tests.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
+ <title>Touch Action Test Page</title>
+ </head>
+ <body>
+ <div id="events">events: </div>
+ <div id="padding">
+ We need some padding here so that the page is large enough to test swipe
+ and scroll actions.
+ </div>
+ <div id="bottom">This is the bottom of the page.</div>
+ <script>
+ events = document.getElementById('events');
+ var eventTypes = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
+ var eventListener = function(evt) {
+ events.innerHTML += ' ' + evt.type;
+ };
+
+ for (var i = 0; i < eventTypes.length; i++) {
+ events.addEventListener(eventTypes[i], eventListener);
+ }
+
+ var padding = document.getElementById('padding');
+ padding.style.border = 'solid';
+ padding.style.height = window.screen.height * 2 + 'px';
+ </script>
+ </body>
+</html>
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index cbbd46a..cbae682 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -6,10 +6,12 @@
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
+#include "base/values.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/compositor/compositor_switches.h"
@@ -67,10 +69,6 @@ class DevToolsProtocolTest : public ContentBrowserTest,
return false;
}
- scoped_ptr<base::DictionaryValue> result_;
- scoped_refptr<DevToolsAgentHost> agent_host_;
-
- private:
void SetUpOnMainThread() override {
agent_host_ = DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
agent_host_->AttachClient(this);
@@ -81,6 +79,10 @@ class DevToolsProtocolTest : public ContentBrowserTest,
agent_host_ = NULL;
}
+ scoped_ptr<base::DictionaryValue> result_;
+ scoped_refptr<DevToolsAgentHost> agent_host_;
+
+ private:
void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) override {
scoped_ptr<base::DictionaryValue> root(
@@ -135,4 +137,128 @@ IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, MAYBE_CaptureScreenshot) {
EXPECT_TRUE(std::abs(0x56-(int)SkColorGetB(color)) <= 1);
}
+class SyntheticGestureTest : public DevToolsProtocolTest {
+#if !defined(OS_ANDROID)
+ protected:
+ void SetUpOnMainThread() override {
+ DevToolsProtocolTest::SetUpOnMainThread();
+
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetInteger("width", 384);
+ params->SetInteger("height", 640);
+ params->SetDouble("deviceScaleFactor", 2.0);
+ params->SetBoolean("mobile", true);
+ params->SetBoolean("fitWindow", false);
+ params->SetBoolean("textAutosizing", true);
+ SendCommand("Page.setDeviceMetricsOverride", params.Pass());
+
+ params.reset(new base::DictionaryValue());
+ params->SetBoolean("enabled", true);
+ params->SetString("configuration", "mobile");
+ SendCommand("Page.setTouchEmulationEnabled", params.Pass());
+ }
+#endif
+};
+
+#if defined(OS_ANDROID)
+// crbug.com/469947
+#define MAYBE_SynthesizePinchGesture DISABLED_SynthesizePinchGesture
+#else
+// crbug.com/460128
+#define MAYBE_SynthesizePinchGesture DISABLED_SynthesizePinchGesture
+#endif
+IN_PROC_BROWSER_TEST_F(SyntheticGestureTest, MAYBE_SynthesizePinchGesture) {
+ GURL test_url = GetTestUrl("devtools", "synthetic_gesture_tests.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
+
+ int old_width;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(window.innerWidth)", &old_width));
+
+ int old_height;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(window.innerHeight)", &old_height));
+
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetInteger("x", old_width / 2);
+ params->SetInteger("y", old_height / 2);
+ params->SetDouble("scaleFactor", 2.0);
+ SendCommand("Input.synthesizePinchGesture", params.Pass());
+
+ int new_width;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(window.innerWidth)", &new_width));
+ ASSERT_DOUBLE_EQ(2.0, static_cast<double>(old_width) / new_width);
+
+ int new_height;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(window.innerHeight)", &new_height));
+ ASSERT_DOUBLE_EQ(2.0, static_cast<double>(old_height) / new_height);
+}
+
+#if defined(OS_ANDROID)
+#define MAYBE_SynthesizeScrollGesture SynthesizeScrollGesture
+#else
+// crbug.com/460128
+#define MAYBE_SynthesizeScrollGesture DISABLED_SynthesizeScrollGesture
+#endif
+IN_PROC_BROWSER_TEST_F(SyntheticGestureTest, MAYBE_SynthesizeScrollGesture) {
+ GURL test_url = GetTestUrl("devtools", "synthetic_gesture_tests.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
+
+ int scroll_top;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(document.body.scrollTop)", &scroll_top));
+ ASSERT_EQ(0, scroll_top);
+
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetInteger("x", 0);
+ params->SetInteger("y", 0);
+ params->SetInteger("xDistance", 0);
+ params->SetInteger("yDistance", -100);
+ SendCommand("Input.synthesizeScrollGesture", params.Pass());
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(document.body.scrollTop)", &scroll_top));
+ ASSERT_EQ(100, scroll_top);
+}
+
+#if defined(OS_ANDROID)
+#define MAYBE_SynthesizeTapGesture SynthesizeTapGesture
+#else
+// crbug.com/460128
+#define MAYBE_SynthesizeTapGesture DISABLED_SynthesizeTapGesture
+#endif
+IN_PROC_BROWSER_TEST_F(SyntheticGestureTest, MAYBE_SynthesizeTapGesture) {
+ GURL test_url = GetTestUrl("devtools", "synthetic_gesture_tests.html");
+ NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1);
+
+ int scroll_top;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(document.body.scrollTop)", &scroll_top));
+ ASSERT_EQ(0, scroll_top);
+
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetInteger("x", 16);
+ params->SetInteger("y", 16);
+ params->SetString("gestureSourceType", "touch");
+ SendCommand("Input.synthesizeTapGesture", params.Pass());
+
+ // The link that we just tapped should take us to the bottom of the page. The
+ // new value of |document.body.scrollTop| will depend on the screen dimensions
+ // of the device that we're testing on, but in any case it should be greater
+ // than 0.
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(),
+ "domAutomationController.send(document.body.scrollTop)", &scroll_top));
+ ASSERT_GT(scroll_top, 0);
+}
+
} // namespace content
diff --git a/content/browser/devtools/protocol/input_handler.cc b/content/browser/devtools/protocol/input_handler.cc
index 4c460e9..0f15928 100644
--- a/content/browser/devtools/protocol/input_handler.cc
+++ b/content/browser/devtools/protocol/input_handler.cc
@@ -6,14 +6,47 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "cc/output/compositor_frame_metadata.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/input/synthetic_pinch_gesture_params.h"
+#include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
+#include "content/common/input/synthetic_tap_gesture_params.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/keycodes/dom4/keycode_converter.h"
+#include "ui/gfx/geometry/point.h"
namespace content {
namespace devtools {
namespace input {
+namespace {
+
+gfx::Point CssPixelsToPoint(int x, int y, float page_scale_factor) {
+ return gfx::Point(x * page_scale_factor, y * page_scale_factor);
+}
+
+gfx::Vector2d CssPixelsToVector2d(int x, int y, float page_scale_factor) {
+ return gfx::Vector2d(x * page_scale_factor, y * page_scale_factor);
+}
+
+bool StringToGestureSourceType(const std::string& in,
+ SyntheticGestureParams::GestureSourceType& out) {
+ if (in == kGestureSourceTypeDefault) {
+ out = SyntheticGestureParams::GestureSourceType::DEFAULT_INPUT;
+ return true;
+ } else if (in == kGestureSourceTypeTouch) {
+ out = SyntheticGestureParams::GestureSourceType::TOUCH_INPUT;
+ return true;
+ } else if (in == kGestureSourceTypeMouse) {
+ out = SyntheticGestureParams::GestureSourceType::MOUSE_INPUT;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+}
+
typedef DevToolsProtocolClient::Response Response;
namespace {
@@ -87,7 +120,9 @@ bool SetMouseEventType(blink::WebMouseEvent* event, const std::string& type) {
} // namespace
InputHandler::InputHandler()
- : host_(NULL) {
+ : host_(NULL),
+ page_scale_factor_(1.0),
+ weak_factory_(this) {
}
InputHandler::~InputHandler() {
@@ -97,7 +132,14 @@ void InputHandler::SetRenderWidgetHost(RenderWidgetHostImpl* host) {
host_ = host;
}
-void InputHandler::SetClient(scoped_ptr<DevToolsProtocolClient> client) {
+void InputHandler::SetClient(scoped_ptr<Client> client) {
+ client_.swap(client);
+}
+
+void InputHandler::OnSwapCompositorFrame(
+ const cc::CompositorFrameMetadata& frame_metadata) {
+ page_scale_factor_ = frame_metadata.page_scale_factor;
+ scrollable_viewport_size_ = frame_metadata.scrollable_viewport_size;
}
Response InputHandler::DispatchKeyEvent(
@@ -260,7 +302,29 @@ Response InputHandler::SynthesizePinchGesture(
double scale_factor,
const int* relative_speed,
const std::string* gesture_source_type) {
- return Response::InternalError("Not yet implemented");
+ if (!host_)
+ return Response::ServerError("Could not connect to view");
+
+ SyntheticPinchGestureParams gesture_params;
+ const int kDefaultRelativeSpeed = 800;
+
+ gesture_params.scale_factor = scale_factor;
+ gesture_params.anchor = CssPixelsToPoint(x, y, page_scale_factor_);
+ gesture_params.relative_pointer_speed_in_pixels_s =
+ relative_speed ? *relative_speed : kDefaultRelativeSpeed;
+
+ if (!StringToGestureSourceType(
+ gesture_source_type ? *gesture_source_type : kGestureSourceTypeDefault,
+ gesture_params.gesture_source_type)) {
+ return Response::InvalidParams("gestureSourceType");
+ }
+
+ host_->QueueSyntheticGesture(
+ SyntheticGesture::Create(gesture_params),
+ base::Bind(&InputHandler::SendSynthesizePinchGestureResponse,
+ weak_factory_.GetWeakPtr(), command_id));
+
+ return Response::OK();
}
Response InputHandler::SynthesizeScrollGesture(
@@ -274,7 +338,44 @@ Response InputHandler::SynthesizeScrollGesture(
const bool* prevent_fling,
const int* speed,
const std::string* gesture_source_type) {
- return Response::InternalError("Not yet implemented");
+ if (!host_)
+ return Response::ServerError("Could not connect to view");
+
+ SyntheticSmoothScrollGestureParams gesture_params;
+ const bool kDefaultPreventFling = true;
+ const int kDefaultSpeed = 800;
+
+ gesture_params.anchor = CssPixelsToPoint(x, y, page_scale_factor_);
+ gesture_params.prevent_fling =
+ prevent_fling ? *prevent_fling : kDefaultPreventFling;
+ gesture_params.speed_in_pixels_s = speed ? *speed : kDefaultSpeed;
+
+ if (x_distance || y_distance) {
+ gesture_params.distances.push_back(
+ CssPixelsToVector2d(x_distance ? *x_distance : 0,
+ y_distance ? *y_distance : 0,
+ page_scale_factor_));
+ }
+
+ if (x_overscroll || y_overscroll) {
+ gesture_params.distances.push_back(
+ CssPixelsToVector2d(x_overscroll ? -*x_overscroll : 0,
+ y_overscroll ? -*y_overscroll : 0,
+ page_scale_factor_));
+ }
+
+ if (!StringToGestureSourceType(
+ gesture_source_type ? *gesture_source_type : kGestureSourceTypeDefault,
+ gesture_params.gesture_source_type)) {
+ return Response::InvalidParams("gestureSourceType");
+ }
+
+ host_->QueueSyntheticGesture(
+ SyntheticGesture::Create(gesture_params),
+ base::Bind(&InputHandler::SendSynthesizeScrollGestureResponse,
+ weak_factory_.GetWeakPtr(), command_id));
+
+ return Response::OK();
}
Response InputHandler::SynthesizeTapGesture(
@@ -284,7 +385,78 @@ Response InputHandler::SynthesizeTapGesture(
const int* duration,
const int* tap_count,
const std::string* gesture_source_type) {
- return Response::InternalError("Not yet implemented");
+ if (!host_)
+ return Response::ServerError("Could not connect to view");
+
+ SyntheticTapGestureParams gesture_params;
+ const int kDefaultDuration = 50;
+ const int kDefaultTapCount = 1;
+
+ gesture_params.position = CssPixelsToPoint(x, y, page_scale_factor_);
+ gesture_params.duration_ms = duration ? *duration : kDefaultDuration;
+
+ if (!StringToGestureSourceType(
+ gesture_source_type ? *gesture_source_type : kGestureSourceTypeDefault,
+ gesture_params.gesture_source_type)) {
+ return Response::InvalidParams("gestureSourceType");
+ }
+
+ if (!tap_count)
+ tap_count = &kDefaultTapCount;
+
+ for (int i = 0; i < *tap_count; i++) {
+ // If we're doing more than one tap, don't send the response to the client
+ // until we've completed the last tap.
+ bool is_last_tap = i == *tap_count - 1;
+ host_->QueueSyntheticGesture(
+ SyntheticGesture::Create(gesture_params),
+ base::Bind(&InputHandler::SendSynthesizeTapGestureResponse,
+ weak_factory_.GetWeakPtr(), command_id, is_last_tap));
+ }
+
+ return Response::OK();
+}
+
+void InputHandler::SendSynthesizePinchGestureResponse(
+ DevToolsCommandId command_id,
+ SyntheticGesture::Result result) {
+ if (result == SyntheticGesture::Result::GESTURE_FINISHED) {
+ client_->SendSynthesizePinchGestureResponse(
+ command_id, SynthesizePinchGestureResponse::Create());
+ } else {
+ client_->SendError(command_id,
+ Response::InternalError(base::StringPrintf(
+ "Synthetic pinch failed, result was %d", result)));
+ }
+}
+
+void InputHandler::SendSynthesizeScrollGestureResponse(
+ DevToolsCommandId command_id,
+ SyntheticGesture::Result result) {
+ if (result == SyntheticGesture::Result::GESTURE_FINISHED) {
+ client_->SendSynthesizeScrollGestureResponse(
+ command_id, SynthesizeScrollGestureResponse::Create());
+ } else {
+ client_->SendError(command_id,
+ Response::InternalError(base::StringPrintf(
+ "Synthetic scroll failed, result was %d", result)));
+ }
+}
+
+void InputHandler::SendSynthesizeTapGestureResponse(
+ DevToolsCommandId command_id,
+ bool send_success,
+ SyntheticGesture::Result result) {
+ if (result == SyntheticGesture::Result::GESTURE_FINISHED) {
+ if (send_success) {
+ client_->SendSynthesizeTapGestureResponse(
+ command_id, SynthesizeTapGestureResponse::Create());
+ }
+ } else {
+ client_->SendError(command_id,
+ Response::InternalError(base::StringPrintf(
+ "Synthetic tap failed, result was %d", result)));
+ }
}
} // namespace input
diff --git a/content/browser/devtools/protocol/input_handler.h b/content/browser/devtools/protocol/input_handler.h
index cae6fb5..22a1ad7 100644
--- a/content/browser/devtools/protocol/input_handler.h
+++ b/content/browser/devtools/protocol/input_handler.h
@@ -5,7 +5,18 @@
#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_INPUT_HANDLER_H_
#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_INPUT_HANDLER_H_
+#include "base/memory/weak_ptr.h"
#include "content/browser/devtools/protocol/devtools_protocol_handler.h"
+#include "content/browser/renderer_host/input/synthetic_gesture.h"
+#include "ui/gfx/geometry/size_f.h"
+
+namespace cc {
+class CompositorFrameMetadata;
+}
+
+namespace gfx {
+class Point;
+}
namespace content {
@@ -22,7 +33,8 @@ class InputHandler {
virtual ~InputHandler();
void SetRenderWidgetHost(RenderWidgetHostImpl* host);
- void SetClient(scoped_ptr<DevToolsProtocolClient> client);
+ void SetClient(scoped_ptr<Client> client);
+ void OnSwapCompositorFrame(const cc::CompositorFrameMetadata& frame_metadata);
Response DispatchKeyEvent(const std::string& type,
const int* modifiers,
@@ -81,7 +93,21 @@ class InputHandler {
const std::string* gesture_source_type);
private:
+ void SendSynthesizePinchGestureResponse(DevToolsCommandId command_id,
+ SyntheticGesture::Result result);
+
+ void SendSynthesizeScrollGestureResponse(DevToolsCommandId command_id,
+ SyntheticGesture::Result result);
+
+ void SendSynthesizeTapGestureResponse(DevToolsCommandId command_id,
+ bool send_success,
+ SyntheticGesture::Result result);
+
RenderWidgetHostImpl* host_;
+ scoped_ptr<Client> client_;
+ float page_scale_factor_;
+ gfx::SizeF scrollable_viewport_size_;
+ base::WeakPtrFactory<InputHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(InputHandler);
};
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 409ee0d..6f40d6f 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -494,6 +494,8 @@ void RenderFrameDevToolsAgentHost::OnSwapCompositorFrame(
return;
if (page_handler_)
page_handler_->OnSwapCompositorFrame(get<1>(param).metadata);
+ if (input_handler_)
+ input_handler_->OnSwapCompositorFrame(get<1>(param).metadata);
if (frame_trace_recorder_) {
frame_trace_recorder_->OnSwapCompositorFrame(
render_frame_host_, get<1>(param).metadata);
@@ -506,6 +508,8 @@ void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
return;
if (page_handler_)
page_handler_->OnSwapCompositorFrame(frame_metadata);
+ if (input_handler_)
+ input_handler_->OnSwapCompositorFrame(frame_metadata);
if (frame_trace_recorder_) {
frame_trace_recorder_->OnSwapCompositorFrame(
render_frame_host_, frame_metadata);
diff --git a/content/test/data/devtools/synthetic_gesture_tests.html b/content/test/data/devtools/synthetic_gesture_tests.html
new file mode 100644
index 0000000..280153d
--- /dev/null
+++ b/content/test/data/devtools/synthetic_gesture_tests.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
+ <title>Synthetic Gesture Test Page</title>
+ </head>
+ <body>
+ <a href="#bottom">Go to the bottom of the page</a>
+ <div id="padding">
+ We need some padding here so that the page is large enough to test swipe
+ and scroll actions.
+ </div>
+ <div id="bottom"><a name="bottom">This is the bottom of the page.</a></div>
+ <script>
+ var padding = document.getElementById('padding');
+ padding.style.border = 'solid';
+ padding.style.height = window.screen.height * 2 + 'px';
+ </script>
+ </body>
+</html>