// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/devtools/protocol/emulation_handler.h" #include "base/strings/string_number_conversions.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/geolocation/geolocation_service_context.h" #include "content/browser/renderer_host/render_widget_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; 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::ScreencastEnabledChanged() { UpdateTouchEventEmulationState(); } void EmulationHandler::SetRenderFrameHost(RenderFrameHostImpl* 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) { if (!GetWebContents()) return Response::InternalError("Could not connect to view"); GeolocationServiceContext* geolocation_context = GetWebContents()->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() { if (!GetWebContents()) return Response::InternalError("Could not connect to view"); GeolocationServiceContext* geolocation_context = GetWebContents()->GetGeolocationServiceContext(); geolocation_context->ClearOverride(); return Response::OK(); } Response EmulationHandler::SetTouchEmulationEnabled( bool enabled, const std::string* configuration) { touch_emulation_enabled_ = enabled; touch_emulation_configuration_ = configuration ? *configuration : std::string(); UpdateTouchEventEmulationState(); return Response::FallThrough(); } Response EmulationHandler::CanEmulate(bool* result) { #if defined(OS_ANDROID) *result = false; #else *result = true; if (WebContentsImpl* web_contents = GetWebContents()) *result &= !web_contents->GetVisibleURL().SchemeIs(kChromeDevToolsScheme); #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) { 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() { if (!device_emulation_enabled_) return Response::OK(); device_emulation_enabled_ = false; UpdateDeviceEmulationState(); return Response::OK(); } WebContentsImpl* EmulationHandler::GetWebContents() { return host_ ? static_cast(WebContents::FromRenderFrameHost(host_)) : nullptr; } void EmulationHandler::UpdateTouchEventEmulationState() { RenderWidgetHostImpl* widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr; if (!host_) return; bool enabled = touch_emulation_enabled_ || page_handler_->screencast_enabled(); ui::GestureProviderConfigType config_type = TouchEmulationConfigurationToType(touch_emulation_configuration_); widget_host->SetTouchEventEmulationEnabled(enabled, config_type); if (GetWebContents()) GetWebContents()->SetForceDisableOverscrollContent(enabled); } void EmulationHandler::UpdateDeviceEmulationState() { RenderWidgetHostImpl* widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr; if (!host_) return; if (device_emulation_enabled_) { widget_host->Send(new ViewMsg_EnableDeviceEmulation( widget_host->GetRoutingID(), device_emulation_params_)); } else { widget_host->Send(new ViewMsg_DisableDeviceEmulation( widget_host->GetRoutingID())); } } } // namespace emulation } // namespace devtools } // namespace content