| // 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 <utility> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "build/build_config.h" |
| #include "content/browser/frame_host/render_frame_host_impl.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" |
| #include "device/geolocation/geolocation_service_context.h" |
| #include "device/geolocation/geoposition.h" |
| |
| namespace content { |
| namespace protocol { |
| |
| using GeolocationServiceContext = device::GeolocationServiceContext; |
| using Geoposition = device::Geoposition; |
| |
| namespace { |
| |
| blink::WebScreenOrientationType WebScreenOrientationTypeFromString( |
| const std::string& type) { |
| if (type == Emulation::ScreenOrientation::TypeEnum::PortraitPrimary) |
| return blink::WebScreenOrientationPortraitPrimary; |
| if (type == Emulation::ScreenOrientation::TypeEnum::PortraitSecondary) |
| return blink::WebScreenOrientationPortraitSecondary; |
| if (type == Emulation::ScreenOrientation::TypeEnum::LandscapePrimary) |
| return blink::WebScreenOrientationLandscapePrimary; |
| if (type == Emulation::ScreenOrientation::TypeEnum::LandscapeSecondary) |
| return blink::WebScreenOrientationLandscapeSecondary; |
| return blink::WebScreenOrientationUndefined; |
| } |
| |
| ui::GestureProviderConfigType TouchEmulationConfigurationToType( |
| const std::string& protocol_value) { |
| ui::GestureProviderConfigType result = |
| ui::GestureProviderConfigType::CURRENT_PLATFORM; |
| if (protocol_value == |
| Emulation::SetTouchEmulationEnabled::ConfigurationEnum::Mobile) { |
| result = ui::GestureProviderConfigType::GENERIC_MOBILE; |
| } |
| if (protocol_value == |
| Emulation::SetTouchEmulationEnabled::ConfigurationEnum::Desktop) { |
| result = ui::GestureProviderConfigType::GENERIC_DESKTOP; |
| } |
| return result; |
| } |
| |
| } // namespace |
| |
| EmulationHandler::EmulationHandler() |
| : DevToolsDomainHandler(Emulation::Metainfo::domainName), |
| touch_emulation_enabled_(false), |
| device_emulation_enabled_(false), |
| host_(nullptr) { |
| } |
| |
| EmulationHandler::~EmulationHandler() { |
| } |
| |
| void EmulationHandler::SetRenderFrameHost(RenderFrameHostImpl* host) { |
| if (host_ == host) |
| return; |
| |
| host_ = host; |
| UpdateTouchEventEmulationState(); |
| UpdateDeviceEmulationState(); |
| } |
| |
| void EmulationHandler::Wire(UberDispatcher* dispatcher) { |
| Emulation::Dispatcher::wire(dispatcher, this); |
| } |
| |
| Response EmulationHandler::Disable() { |
| touch_emulation_enabled_ = false; |
| device_emulation_enabled_ = false; |
| UpdateTouchEventEmulationState(); |
| UpdateDeviceEmulationState(); |
| return Response::OK(); |
| } |
| |
| Response EmulationHandler::SetGeolocationOverride( |
| Maybe<double> latitude, Maybe<double> longitude, Maybe<double> accuracy) { |
| if (!GetWebContents()) |
| return Response::InternalError(); |
| |
| GeolocationServiceContext* geolocation_context = |
| GetWebContents()->GetGeolocationServiceContext(); |
| std::unique_ptr<Geoposition> geoposition(new Geoposition()); |
| if (latitude.isJust() && longitude.isJust() && accuracy.isJust()) { |
| geoposition->latitude = latitude.fromJust(); |
| geoposition->longitude = longitude.fromJust(); |
| geoposition->accuracy = accuracy.fromJust(); |
| geoposition->timestamp = base::Time::Now(); |
| if (!geoposition->Validate()) |
| return Response::Error("Invalid geolocation"); |
| } else { |
| geoposition->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; |
| } |
| geolocation_context->SetOverride(std::move(geoposition)); |
| return Response::OK(); |
| } |
| |
| Response EmulationHandler::ClearGeolocationOverride() { |
| if (!GetWebContents()) |
| return Response::InternalError(); |
| |
| GeolocationServiceContext* geolocation_context = |
| GetWebContents()->GetGeolocationServiceContext(); |
| geolocation_context->ClearOverride(); |
| return Response::OK(); |
| } |
| |
| Response EmulationHandler::SetTouchEmulationEnabled( |
| bool enabled, Maybe<std::string> configuration) { |
| touch_emulation_enabled_ = enabled; |
| touch_emulation_configuration_ = configuration.fromMaybe(""); |
| 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, |
| Maybe<double> scale, |
| Maybe<double> offset_x, |
| Maybe<double> offset_y, |
| Maybe<int> screen_width, |
| Maybe<int> screen_height, |
| Maybe<int> position_x, |
| Maybe<int> position_y, |
| Maybe<Emulation::ScreenOrientation> screen_orientation) { |
| const static int max_size = 10000000; |
| const static double max_scale = 10; |
| const static int max_orientation_angle = 360; |
| |
| if (!host_) |
| return Response::InternalError(); |
| |
| if (screen_width.fromMaybe(0) < 0 || screen_height.fromMaybe(0) < 0 || |
| screen_width.fromMaybe(0) > max_size || |
| screen_height.fromMaybe(0) > max_size) { |
| return Response::InvalidParams( |
| "Screen width and height values must be positive, not greater than " + |
| base::IntToString(max_size)); |
| } |
| |
| if (position_x.fromMaybe(0) < 0 || position_y.fromMaybe(0) < 0 || |
| position_x.fromMaybe(0) > screen_width.fromMaybe(0) || |
| position_y.fromMaybe(0) > screen_height.fromMaybe(0)) { |
| 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 (scale.fromMaybe(1) <= 0 || scale.fromMaybe(1) > max_scale) { |
| return Response::InvalidParams( |
| "scale must be positive, not greater than " + |
| base::DoubleToString(max_scale)); |
| } |
| |
| blink::WebScreenOrientationType orientationType = |
| blink::WebScreenOrientationUndefined; |
| int orientationAngle = 0; |
| if (screen_orientation.isJust()) { |
| Emulation::ScreenOrientation* orientation = screen_orientation.fromJust(); |
| orientationType = WebScreenOrientationTypeFromString( |
| orientation->GetType()); |
| if (orientationType == blink::WebScreenOrientationUndefined) |
| return Response::InvalidParams("Invalid screen orientation type value"); |
| orientationAngle = orientation->GetAngle(); |
| if (orientationAngle < 0 || orientationAngle >= max_orientation_angle) { |
| return Response::InvalidParams( |
| "Screen orientation angle must be non-negative, less than " + |
| base::IntToString(max_orientation_angle)); |
| } |
| } |
| |
| blink::WebDeviceEmulationParams params; |
| params.screenPosition = mobile ? blink::WebDeviceEmulationParams::Mobile : |
| blink::WebDeviceEmulationParams::Desktop; |
| params.screenSize = blink::WebSize(screen_width.fromMaybe(0), |
| screen_height.fromMaybe(0)); |
| params.viewPosition = blink::WebPoint(position_x.fromMaybe(0), |
| position_y.fromMaybe(0)); |
| params.deviceScaleFactor = device_scale_factor; |
| params.viewSize = blink::WebSize(width, height); |
| params.fitToView = fit_window; |
| params.scale = scale.fromMaybe(1); |
| params.screenOrientationType = orientationType; |
| params.screenOrientationAngle = orientationAngle; |
| |
| 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(); |
| } |
| |
| Response EmulationHandler::SetVisibleSize(int width, int height) { |
| if (width < 0 || height < 0) |
| return Response::InvalidParams("Width and height must be non-negative"); |
| |
| // Set size of frame by resizing RWHV if available. |
| RenderWidgetHostImpl* widget_host = |
| host_ ? host_->GetRenderWidgetHost() : nullptr; |
| if (!widget_host) |
| return Response::Error("Target does not support setVisibleSize"); |
| |
| widget_host->GetView()->SetSize(gfx::Size(width, height)); |
| 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_; |
| 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 protocol |
| } // namespace content |