diff options
Diffstat (limited to 'content/browser/devtools/protocol')
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); }; |