From 23ca53245b79c816a5bc0e969a5b3393107fffdd Mon Sep 17 00:00:00 2001 From: dgozman Date: Thu, 19 Mar 2015 05:26:10 -0700 Subject: [PATCH] [DevTools] Emulation domain implementation (browser side). BUG=467059 Review URL: https://codereview.chromium.org/1014403002 Cr-Commit-Position: refs/heads/master@{#321339} --- .../browser/devtools/protocol/emulation_handler.cc | 187 ++++++++++++++++++++- .../browser/devtools/protocol/emulation_handler.h | 34 +++- content/browser/devtools/protocol/page_handler.cc | 11 ++ content/browser/devtools/protocol/page_handler.h | 9 + .../devtools/render_frame_devtools_agent_host.cc | 7 + .../devtools/render_frame_devtools_agent_host.h | 2 + 6 files changed, 237 insertions(+), 13 deletions(-) diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc index 71e568803705..c57d8999dacc 100644 --- a/content/browser/devtools/protocol/emulation_handler.cc +++ b/content/browser/devtools/protocol/emulation_handler.cc @@ -4,48 +4,219 @@ #include "content/browser/devtools/protocol/emulation_handler.h" +#include "base/strings/string_number_conversions.h" +#include "content/browser/geolocation/geolocation_service_context.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/view_messages.h" +#include "content/public/common/url_constants.h" + namespace content { namespace devtools { namespace emulation { using Response = DevToolsProtocolClient::Response; -EmulationHandler::EmulationHandler() { +namespace { + +ui::GestureProviderConfigType TouchEmulationConfigurationToType( + const std::string& protocol_value) { + ui::GestureProviderConfigType result = + ui::GestureProviderConfigType::CURRENT_PLATFORM; + if (protocol_value == + set_touch_emulation_enabled::kConfigurationMobile) { + result = ui::GestureProviderConfigType::GENERIC_MOBILE; + } + if (protocol_value == + set_touch_emulation_enabled::kConfigurationDesktop) { + result = ui::GestureProviderConfigType::GENERIC_DESKTOP; + } + return result; +} + +} // namespace + +EmulationHandler::EmulationHandler(page::PageHandler* page_handler) + : touch_emulation_enabled_(false), + device_emulation_enabled_(false), + page_handler_(page_handler), + host_(nullptr) +{ + page_handler->SetScreencastListener(this); } EmulationHandler::~EmulationHandler() { } -void EmulationHandler::SetClient(scoped_ptr client) { +void EmulationHandler::ScreencastEnabledChanged() { + UpdateTouchEventEmulationState(); +} + +void EmulationHandler::SetRenderViewHost(RenderViewHostImpl* host) { + if (host_ == host) + return; + + host_ = host; + UpdateTouchEventEmulationState(); + UpdateDeviceEmulationState(); +} + +void EmulationHandler::Detached() { + touch_emulation_enabled_ = false; + device_emulation_enabled_ = false; + UpdateTouchEventEmulationState(); + UpdateDeviceEmulationState(); } Response EmulationHandler::SetGeolocationOverride( double* latitude, double* longitude, double* accuracy) { - return Response::InternalError("Not implemented"); + if (!host_) + return Response::InternalError("Could not connect to view"); + + WebContentsImpl* web_contents = static_cast( + WebContents::FromRenderViewHost(host_)); + if (!web_contents) + return Response::InternalError("No WebContents to override"); + + GeolocationServiceContext* geolocation_context = + web_contents->GetGeolocationServiceContext(); + scoped_ptr geoposition(new Geoposition()); + if (latitude && longitude && accuracy) { + geoposition->latitude = *latitude; + geoposition->longitude = *longitude; + geoposition->accuracy = *accuracy; + geoposition->timestamp = base::Time::Now(); + if (!geoposition->Validate()) { + return Response::InternalError("Invalid geolocation"); + } + } else { + geoposition->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + } + geolocation_context->SetOverride(geoposition.Pass()); + return Response::OK(); } Response EmulationHandler::ClearGeolocationOverride() { - return Response::InternalError("Not implemented"); + if (!host_) + return Response::InternalError("Could not connect to view"); + + WebContentsImpl* web_contents = static_cast( + WebContents::FromRenderViewHost(host_)); + if (!web_contents) + return Response::InternalError("No WebContents to override"); + + GeolocationServiceContext* geolocation_context = + web_contents->GetGeolocationServiceContext(); + geolocation_context->ClearOverride(); + return Response::OK(); } Response EmulationHandler::SetTouchEmulationEnabled( bool enabled, const std::string* configuration) { - return Response::InternalError("Not implemented"); + touch_emulation_enabled_ = enabled; + touch_emulation_configuration_ = + configuration ? *configuration : std::string(); + UpdateTouchEventEmulationState(); + return Response::FallThrough(); } Response EmulationHandler::CanEmulate(bool* result) { - return Response::InternalError("Not implemented"); +#if defined(OS_ANDROID) + *result = false; +#else + if (host_) { + if (WebContents* web_contents = WebContents::FromRenderViewHost(host_)) { + *result = web_contents->GetMainFrame()->GetRenderViewHost() == host_; +#if defined(DEBUG_DEVTOOLS) + *result &= !web_contents->GetVisibleURL().SchemeIs(kChromeDevToolsScheme); +#endif // defined(DEBUG_DEVTOOLS) + } else { + *result = true; + } + } else { + *result = true; + } +#endif // defined(OS_ANDROID) + return Response::OK(); } Response EmulationHandler::SetDeviceMetricsOverride( int width, int height, double device_scale_factor, bool mobile, bool fit_window, const double* optional_scale, const double* optional_offset_x, const double* optional_offset_y) { - return Response::InternalError("Not implemented"); + const static int max_size = 10000000; + const static double max_scale = 10; + + if (!host_) + return Response::InternalError("Could not connect to view"); + + if (width < 0 || height < 0 || width > max_size || height > max_size) { + return Response::InvalidParams( + "Width and height values must be positive, not greater than " + + base::IntToString(max_size)); + } + + if (device_scale_factor < 0) + return Response::InvalidParams("deviceScaleFactor must be non-negative"); + + if (optional_scale && (*optional_scale <= 0 || *optional_scale > max_scale)) { + return Response::InvalidParams( + "scale must be positive, not greater than " + + base::IntToString(max_scale)); + } + + blink::WebDeviceEmulationParams params; + params.screenPosition = mobile ? blink::WebDeviceEmulationParams::Mobile : + blink::WebDeviceEmulationParams::Desktop; + params.deviceScaleFactor = device_scale_factor; + params.viewSize = blink::WebSize(width, height); + params.fitToView = fit_window; + params.scale = optional_scale ? *optional_scale : 1; + params.offset = blink::WebFloatPoint( + optional_offset_x ? *optional_offset_x : 0.f, + optional_offset_y ? *optional_offset_y : 0.f); + + if (device_emulation_enabled_ && params == device_emulation_params_) + return Response::OK(); + + device_emulation_enabled_ = true; + device_emulation_params_ = params; + UpdateDeviceEmulationState(); + return Response::OK(); } Response EmulationHandler::ClearDeviceMetricsOverride() { - return Response::InternalError("Not implemented"); + if (!device_emulation_enabled_) + return Response::OK(); + + device_emulation_enabled_ = false; + UpdateDeviceEmulationState(); + return Response::OK(); +} + +void EmulationHandler::UpdateTouchEventEmulationState() { + if (!host_) + return; + bool enabled = touch_emulation_enabled_ || + page_handler_->screencast_enabled(); + ui::GestureProviderConfigType config_type = + TouchEmulationConfigurationToType(touch_emulation_configuration_); + host_->SetTouchEventEmulationEnabled(enabled, config_type); + WebContentsImpl* web_contents = static_cast( + WebContents::FromRenderViewHost(host_)); + if (web_contents) + web_contents->SetForceDisableOverscrollContent(enabled); +} + +void EmulationHandler::UpdateDeviceEmulationState() { + if (!host_) + return; + if (device_emulation_enabled_) { + host_->Send(new ViewMsg_EnableDeviceEmulation( + host_->GetRoutingID(), device_emulation_params_)); + } else { + host_->Send(new ViewMsg_DisableDeviceEmulation(host_->GetRoutingID())); + } } } // namespace emulation diff --git a/content/browser/devtools/protocol/emulation_handler.h b/content/browser/devtools/protocol/emulation_handler.h index 8f152d37c020..8f14b2f66b91 100644 --- a/content/browser/devtools/protocol/emulation_handler.h +++ b/content/browser/devtools/protocol/emulation_handler.h @@ -6,17 +6,31 @@ #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_EMULATION_HANDLER_H_ #include "content/browser/devtools/protocol/devtools_protocol_handler.h" +#include "content/browser/devtools/protocol/page_handler.h" +#include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" namespace content { + +class RenderViewHostImpl; + namespace devtools { + +namespace page { class PageHandler; } + namespace emulation { -class EmulationHandler { +class EmulationHandler : public page::PageHandler::ScreencastListener { public: using Response = DevToolsProtocolClient::Response; - EmulationHandler(); - virtual ~EmulationHandler(); + explicit EmulationHandler(page::PageHandler* page_handler); + ~EmulationHandler() override; + + // page::PageHandler::ScreencastListener implementation. + void ScreencastEnabledChanged() override; + + void SetRenderViewHost(RenderViewHostImpl* host); + void Detached(); Response SetGeolocationOverride(double* latitude, double* longitude, @@ -37,9 +51,19 @@ class EmulationHandler { const double* optional_offset_y); Response ClearDeviceMetricsOverride(); - void SetClient(scoped_ptr client); - private: + void UpdateTouchEventEmulationState(); + void UpdateDeviceEmulationState(); + + bool touch_emulation_enabled_; + std::string touch_emulation_configuration_; + + bool device_emulation_enabled_; + blink::WebDeviceEmulationParams device_emulation_params_; + + page::PageHandler* page_handler_; + RenderViewHostImpl* host_; + DISALLOW_COPY_AND_ASSIGN(EmulationHandler); }; diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc index 8bb70eeba773..c2931a77843e 100644 --- a/content/browser/devtools/protocol/page_handler.cc +++ b/content/browser/devtools/protocol/page_handler.cc @@ -121,6 +121,7 @@ PageHandler::PageHandler() &PageHandler::OnColorPicked, base::Unretained(this)))), frame_recorder_(new FrameRecorder()), host_(nullptr), + screencast_listener_(nullptr), weak_factory_(this) { } @@ -177,6 +178,10 @@ void PageHandler::DidDetachInterstitialPage() { client_->InterstitialHidden(InterstitialHiddenParams::Create()); } +void PageHandler::SetScreencastListener(ScreencastListener* listener) { + screencast_listener_ = listener; +} + Response PageHandler::Enable() { enabled_ = true; return Response::FallThrough(); @@ -190,6 +195,8 @@ Response PageHandler::Disable() { UpdateTouchEventEmulationState(); UpdateDeviceEmulationState(); color_picker_->SetEnabled(false); + if (screencast_listener_) + screencast_listener_->ScreencastEnabledChanged(); return Response::FallThrough(); } @@ -439,12 +446,16 @@ Response PageHandler::StartScreencast(const std::string* format, else host_->Send(new ViewMsg_ForceRedraw(host_->GetRoutingID(), 0)); } + if (screencast_listener_) + screencast_listener_->ScreencastEnabledChanged(); return Response::FallThrough(); } Response PageHandler::StopScreencast() { screencast_enabled_ = false; UpdateTouchEventEmulationState(); + if (screencast_listener_) + screencast_listener_->ScreencastEnabledChanged(); return Response::FallThrough(); } diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h index 6777a6ab4cdd..cee47594bc3b 100644 --- a/content/browser/devtools/protocol/page_handler.h +++ b/content/browser/devtools/protocol/page_handler.h @@ -30,6 +30,12 @@ class PageHandler { public: typedef DevToolsProtocolClient::Response Response; + class ScreencastListener { + public: + virtual ~ScreencastListener() { } + virtual void ScreencastEnabledChanged() = 0; + }; + PageHandler(); virtual ~PageHandler(); @@ -40,6 +46,8 @@ class PageHandler { void OnVisibilityChanged(bool visible); void DidAttachInterstitialPage(); void DidDetachInterstitialPage(); + void SetScreencastListener(ScreencastListener* listener); + bool screencast_enabled() const { return enabled_ && screencast_enabled_; } Response Enable(); Response Disable(); @@ -147,6 +155,7 @@ class PageHandler { RenderViewHostImpl* host_; scoped_ptr client_; + ScreencastListener* screencast_listener_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(PageHandler); diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc index 6e3950612693..676ae216d4fd 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -13,6 +13,7 @@ #include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/protocol/devtools_protocol_handler.h" #include "content/browser/devtools/protocol/dom_handler.h" +#include "content/browser/devtools/protocol/emulation_handler.h" #include "content/browser/devtools/protocol/input_handler.h" #include "content/browser/devtools/protocol/inspector_handler.h" #include "content/browser/devtools/protocol/network_handler.h" @@ -144,6 +145,8 @@ RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(RenderFrameHost* rfh) new devtools::service_worker::ServiceWorkerHandler()), tracing_handler_(new devtools::tracing::TracingHandler( devtools::tracing::TracingHandler::Renderer)), + emulation_handler_(new devtools::emulation::EmulationHandler( + page_handler_.get())), protocol_handler_(new DevToolsProtocolHandler( base::Bind(&RenderFrameDevToolsAgentHost::DispatchOnInspectorFrontend, base::Unretained(this)))), @@ -151,6 +154,7 @@ RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(RenderFrameHost* rfh) reattaching_(false) { DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); dispatcher->SetDOMHandler(dom_handler_.get()); + dispatcher->SetEmulationHandler(emulation_handler_.get()); dispatcher->SetInputHandler(input_handler_.get()); dispatcher->SetInspectorHandler(inspector_handler_.get()); dispatcher->SetNetworkHandler(network_handler_.get()); @@ -240,6 +244,7 @@ void RenderFrameDevToolsAgentHost::OnClientDetached() { #if defined(OS_ANDROID) power_save_blocker_.reset(); #endif + emulation_handler_->Detached(); page_handler_->Detached(); power_handler_->Detached(); service_worker_handler_->Detached(); @@ -432,6 +437,7 @@ void RenderFrameDevToolsAgentHost::SetRenderFrameHost(RenderFrameHost* rfh) { RenderViewHostImpl* rvh = static_cast( rfh->GetRenderViewHost()); dom_handler_->SetRenderViewHost(rvh); + emulation_handler_->SetRenderViewHost(rvh); input_handler_->SetRenderViewHost(rvh); network_handler_->SetRenderViewHost(rvh); page_handler_->SetRenderViewHost(rvh); @@ -453,6 +459,7 @@ void RenderFrameDevToolsAgentHost::ClearRenderFrameHost() { content::Source(rvh)); render_frame_host_ = nullptr; dom_handler_->SetRenderViewHost(nullptr); + emulation_handler_->SetRenderViewHost(nullptr); input_handler_->SetRenderViewHost(nullptr); network_handler_->SetRenderViewHost(nullptr); page_handler_->SetRenderViewHost(nullptr); diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h index 5aa30b443f55..069b4dd942ca 100644 --- a/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/content/browser/devtools/render_frame_devtools_agent_host.h @@ -34,6 +34,7 @@ class PowerSaveBlockerImpl; namespace devtools { namespace dom { class DOMHandler; } +namespace emulation { class EmulationHandler; } namespace input { class InputHandler; } namespace inspector { class InspectorHandler; } namespace network { class NetworkHandler; } @@ -141,6 +142,7 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost scoped_ptr service_worker_handler_; scoped_ptr tracing_handler_; + scoped_ptr emulation_handler_; scoped_ptr protocol_handler_; scoped_ptr frame_trace_recorder_; #if defined(OS_ANDROID) -- 2.11.4.GIT