summaryrefslogtreecommitdiffstats
path: root/content/browser/devtools/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/devtools/protocol')
-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
3 files changed, 334 insertions, 10 deletions
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);
};