| // 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> 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); |
| if (host_ && host_->GetRenderWidgetHost()) |
| *result &= !host_->GetRenderWidgetHost()->auto_resize_enabled(); |
| #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 int* screen_width, |
| const int* screen_height, |
| const int* position_x, |
| const int* position_y) { |
| const static int max_size = 10000000; |
| const static double max_scale = 10; |
| |
| if (!host_) |
| return Response::InternalError("Could not connect to view"); |
| |
| if (screen_width && screen_height && |
| (*screen_width < 0 || *screen_height < 0 || |
| *screen_width > max_size || *screen_height > max_size)) { |
| return Response::InvalidParams( |
| "Screen width and height values must be positive, not greater than " + |
| base::IntToString(max_size)); |
| } |
| |
| if (screen_width && screen_height && position_x && position_y && |
| (*position_x < 0 || *position_y < 0 || |
| *position_x > *screen_width || *position_y > *screen_height)) { |
| return Response::InvalidParams("View position should be on the screen"); |
| } |
| |
| 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; |
| if (screen_width && screen_height) |
| params.screenSize = blink::WebSize(*screen_width, *screen_height); |
| if (position_x && position_y) |
| params.viewPosition = blink::WebPoint(*position_x, *position_y); |
| 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<WebContentsImpl*>(WebContents::FromRenderFrameHost(host_)) : |
| nullptr; |
| } |
| |
| void EmulationHandler::UpdateTouchEventEmulationState() { |
| RenderWidgetHostImpl* widget_host = |
| host_ ? host_->GetRenderWidgetHost() : nullptr; |
| if (!widget_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 (!widget_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 |