|  | // Copyright 2014 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 "chrome/browser/extensions/system_display/display_info_provider_chromeos.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <cmath> | 
|  |  | 
|  | #include "ash/public/interfaces/constants.mojom.h" | 
|  | #include "ash/public/interfaces/cros_display_config.mojom.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "content/public/common/service_manager_connection.h" | 
|  | #include "extensions/common/api/system_display.h" | 
|  | #include "services/service_manager/public/cpp/connector.h" | 
|  | #include "ui/display/display.h" | 
|  | #include "ui/display/screen.h" | 
|  | #include "ui/gfx/geometry/point.h" | 
|  | #include "ui/gfx/geometry/rect.h" | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | namespace system_display = api::system_display; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | int64_t GetDisplayId(const std::string& display_id_str) { | 
|  | int64_t display_id; | 
|  | if (!base::StringToInt64(display_id_str, &display_id)) | 
|  | display_id = display::kInvalidDisplayId; | 
|  | return display_id; | 
|  | } | 
|  |  | 
|  | display::Display GetDisplayForId(const std::string& display_id_str) { | 
|  | int64_t id = GetDisplayId(display_id_str); | 
|  | display::Display display; | 
|  | display::Screen::GetScreen()->GetDisplayWithDisplayId(id, &display); | 
|  | return display; | 
|  | } | 
|  |  | 
|  | ash::mojom::DisplayLayoutPosition GetDisplayLayoutPosition( | 
|  | system_display::LayoutPosition position) { | 
|  | switch (position) { | 
|  | case system_display::LAYOUT_POSITION_TOP: | 
|  | return ash::mojom::DisplayLayoutPosition::kTop; | 
|  | case system_display::LAYOUT_POSITION_RIGHT: | 
|  | return ash::mojom::DisplayLayoutPosition::kRight; | 
|  | case system_display::LAYOUT_POSITION_BOTTOM: | 
|  | return ash::mojom::DisplayLayoutPosition::kBottom; | 
|  | case system_display::LAYOUT_POSITION_LEFT: | 
|  | case system_display::LAYOUT_POSITION_NONE: | 
|  | return ash::mojom::DisplayLayoutPosition::kLeft; | 
|  | } | 
|  | NOTREACHED(); | 
|  | return ash::mojom::DisplayLayoutPosition::kLeft; | 
|  | } | 
|  |  | 
|  | system_display::LayoutPosition GetLayoutPositionFromMojo( | 
|  | ash::mojom::DisplayLayoutPosition position) { | 
|  | switch (position) { | 
|  | case ash::mojom::DisplayLayoutPosition::kTop: | 
|  | return system_display::LayoutPosition::LAYOUT_POSITION_TOP; | 
|  | case ash::mojom::DisplayLayoutPosition::kRight: | 
|  | return system_display::LayoutPosition::LAYOUT_POSITION_RIGHT; | 
|  | case ash::mojom::DisplayLayoutPosition::kBottom: | 
|  | return system_display::LayoutPosition::LAYOUT_POSITION_BOTTOM; | 
|  | case ash::mojom::DisplayLayoutPosition::kLeft: | 
|  | return system_display::LayoutPosition::LAYOUT_POSITION_LEFT; | 
|  | } | 
|  | NOTREACHED(); | 
|  | return system_display::LayoutPosition::LAYOUT_POSITION_LEFT; | 
|  | } | 
|  |  | 
|  | gfx::Insets GetInsets(const system_display::Insets& insets) { | 
|  | return gfx::Insets(insets.top, insets.left, insets.bottom, insets.right); | 
|  | } | 
|  |  | 
|  | // Validates the DisplayProperties input. Does not perform any tests with | 
|  | // DisplayManager dependencies. Returns an error string on failure or nullopt | 
|  | // on success. | 
|  | base::Optional<std::string> ValidateDisplayPropertiesInput( | 
|  | const std::string& display_id_str, | 
|  | const system_display::DisplayProperties& info) { | 
|  | int64_t id = GetDisplayId(display_id_str); | 
|  | if (id == display::kInvalidDisplayId) | 
|  | return "Invalid display id"; | 
|  |  | 
|  | const display::Display& primary = | 
|  | display::Screen::GetScreen()->GetPrimaryDisplay(); | 
|  | bool is_primary = id == primary.id() || (info.is_primary && *info.is_primary); | 
|  |  | 
|  | if (info.is_unified) { | 
|  | if (!is_primary) | 
|  | return "Unified desktop mode can only be set for the primary display."; | 
|  | // Setting isUnfied may change the display layout so no other properties | 
|  | // should be set. | 
|  | if (info.mirroring_source_id) | 
|  | return "Unified desktop mode can not be set with mirroringSourceId."; | 
|  | if (info.bounds_origin_x || info.bounds_origin_y || info.rotation || | 
|  | info.overscan || info.display_mode || info.display_zoom_factor) { | 
|  | LOG(WARNING) | 
|  | << "Unified mode set with other properties which will be ignored."; | 
|  | } | 
|  | return base::nullopt; | 
|  | } | 
|  |  | 
|  | // If mirroring source parameter is specified, no other properties should be | 
|  | // set the display list may change when mirroring is applied. | 
|  | if (info.mirroring_source_id && | 
|  | (info.is_primary || info.bounds_origin_x || info.bounds_origin_y || | 
|  | info.rotation || info.overscan || info.display_mode || | 
|  | info.display_zoom_factor)) { | 
|  | return "No other parameter should be set with mirroringSourceId."; | 
|  | } | 
|  |  | 
|  | // Verify the rotation value is valid. | 
|  | if (info.rotation && !display::Display::IsValidRotation(*info.rotation)) | 
|  | return "Invalid rotation."; | 
|  |  | 
|  | return base::nullopt; | 
|  | } | 
|  |  | 
|  | system_display::DisplayMode GetDisplayModeFromMojo( | 
|  | const ash::mojom::DisplayMode mode) { | 
|  | system_display::DisplayMode result; | 
|  | result.width = mode.size.width(); | 
|  | result.height = mode.size.height(); | 
|  | result.width_in_native_pixels = mode.size_in_native_pixels.width(); | 
|  | result.height_in_native_pixels = mode.size_in_native_pixels.height(); | 
|  | result.ui_scale = mode.ui_scale; | 
|  | result.device_scale_factor = mode.device_scale_factor; | 
|  | result.refresh_rate = mode.refresh_rate; | 
|  | result.is_native = mode.is_native; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | system_display::DisplayUnitInfo GetDisplayUnitInfoFromMojo( | 
|  | const ash::mojom::DisplayUnitInfo& mojo_info) { | 
|  | system_display::DisplayUnitInfo info; | 
|  | info.id = mojo_info.id; | 
|  | info.name = mojo_info.name; | 
|  | if (mojo_info.edid) { | 
|  | info.edid = std::make_unique<system_display::Edid>(); | 
|  | info.edid->manufacturer_id = mojo_info.edid->manufacturer_id; | 
|  | info.edid->product_id = mojo_info.edid->product_id; | 
|  | info.edid->year_of_manufacture = mojo_info.edid->year_of_manufacture; | 
|  | } | 
|  | info.is_primary = mojo_info.is_primary; | 
|  | info.is_internal = mojo_info.is_internal; | 
|  | info.is_enabled = mojo_info.is_enabled; | 
|  | info.is_tablet_mode = std::make_unique<bool>(mojo_info.is_tablet_mode); | 
|  | info.dpi_x = mojo_info.dpi_x; | 
|  | info.dpi_y = mojo_info.dpi_y; | 
|  | info.rotation = display::Display::RotationToDegrees(mojo_info.rotation); | 
|  | const gfx::Rect& bounds = mojo_info.bounds; | 
|  | info.bounds.left = bounds.x(); | 
|  | info.bounds.top = bounds.y(); | 
|  | info.bounds.width = bounds.width(); | 
|  | info.bounds.height = bounds.height(); | 
|  | const gfx::Insets& overscan = mojo_info.overscan; | 
|  | info.overscan.left = overscan.left(); | 
|  | info.overscan.top = overscan.top(); | 
|  | info.overscan.right = overscan.right(); | 
|  | info.overscan.bottom = overscan.bottom(); | 
|  | const gfx::Rect& work_area = mojo_info.work_area; | 
|  | info.work_area.left = work_area.x(); | 
|  | info.work_area.top = work_area.y(); | 
|  | info.work_area.width = work_area.width(); | 
|  | info.work_area.height = work_area.height(); | 
|  | for (const ash::mojom::DisplayModePtr& mode : | 
|  | mojo_info.available_display_modes) { | 
|  | info.modes.emplace_back(GetDisplayModeFromMojo(*mode)); | 
|  | } | 
|  | if (!info.modes.empty()) { | 
|  | int index = mojo_info.selected_display_mode_index; | 
|  | if (index < 0 || index >= static_cast<int>(info.modes.size())) | 
|  | index = 0; | 
|  | info.modes[index].is_selected = true; | 
|  | } | 
|  | info.has_touch_support = mojo_info.has_touch_support; | 
|  | info.has_accelerometer_support = mojo_info.has_accelerometer_support; | 
|  | info.available_display_zoom_factors = | 
|  | mojo_info.available_display_zoom_factors; | 
|  | info.display_zoom_factor = mojo_info.display_zoom_factor; | 
|  | return info; | 
|  | } | 
|  |  | 
|  | ash::mojom::TouchCalibrationPairPtr GetTouchCalibrationPair( | 
|  | const system_display::TouchCalibrationPair& pair) { | 
|  | auto result = ash::mojom::TouchCalibrationPair::New(); | 
|  | result->display_point = | 
|  | gfx::Point(pair.display_point.x, pair.display_point.y); | 
|  | result->touch_point = gfx::Point(pair.touch_point.x, pair.touch_point.y); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void SetDisplayUnitInfoLayoutProperties( | 
|  | const ash::mojom::DisplayLayoutInfo& layout, | 
|  | system_display::DisplayUnitInfo* display) { | 
|  | display->is_unified = | 
|  | layout.layout_mode == ash::mojom::DisplayLayoutMode::kUnified; | 
|  | if (layout.mirror_source_id) { | 
|  | display->mirroring_source_id = *layout.mirror_source_id; | 
|  | if (layout.mirror_destination_ids) { | 
|  | for (const std::string& id : *layout.mirror_destination_ids) | 
|  | display->mirroring_destination_ids.push_back(id); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void RunResultCallback(DisplayInfoProvider::ErrorCallback callback, | 
|  | base::Optional<std::string> error) { | 
|  | if (error) | 
|  | LOG(ERROR) << "API call failed: " << *error; | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(std::move(callback), std::move(error))); | 
|  | } | 
|  |  | 
|  | base::Optional<std::string> GetStringResult( | 
|  | ash::mojom::DisplayConfigResult result) { | 
|  | switch (result) { | 
|  | case ash::mojom::DisplayConfigResult::kSuccess: | 
|  | return base::nullopt; | 
|  | case ash::mojom::DisplayConfigResult::kInvalidOperationError: | 
|  | return "Invalid operation"; | 
|  | case ash::mojom::DisplayConfigResult::kInvalidDisplayIdError: | 
|  | return "Invalid display id"; | 
|  | case ash::mojom::DisplayConfigResult::kUnifiedNotEnabledError: | 
|  | return "enableUnifiedDesktop must be called before setting is_unified"; | 
|  | case ash::mojom::DisplayConfigResult::kPropertyValueOutOfRangeError: | 
|  | return "Property value out of range"; | 
|  | case ash::mojom::DisplayConfigResult::kNotSupportedOnInternalDisplayError: | 
|  | return "Not supported for internal displays"; | 
|  | case ash::mojom::DisplayConfigResult::kNegativeValueError: | 
|  | return "Negative values not supported"; | 
|  | case ash::mojom::DisplayConfigResult::kSetDisplayModeError: | 
|  | return "Setting the display mode failed"; | 
|  | case ash::mojom::DisplayConfigResult::kInvalidDisplayLayoutError: | 
|  | return "Invalid display layout"; | 
|  | case ash::mojom::DisplayConfigResult::kMirrorModeSingleDisplayError: | 
|  | return "Mirror mode requires multiple displays"; | 
|  | case ash::mojom::DisplayConfigResult::kMirrorModeSourceIdError: | 
|  | return "Mirror mode source id invalid"; | 
|  | case ash::mojom::DisplayConfigResult::kMirrorModeDestIdError: | 
|  | return "Mirror mode destination id invalid"; | 
|  | case ash::mojom::DisplayConfigResult::kCalibrationNotAvailableError: | 
|  | return "Calibration not available"; | 
|  | case ash::mojom::DisplayConfigResult::kCalibrationNotStartedError: | 
|  | return "Calibration not started"; | 
|  | case ash::mojom::DisplayConfigResult::kCalibrationInProgressError: | 
|  | return "Calibration in progress"; | 
|  | case ash::mojom::DisplayConfigResult::kCalibrationInvalidDataError: | 
|  | return "Calibration data invalid"; | 
|  | case ash::mojom::DisplayConfigResult::kCalibrationFailedError: | 
|  | return "Calibration failed"; | 
|  | } | 
|  | return "Unknown error"; | 
|  | } | 
|  |  | 
|  | void LogErrorResult(ash::mojom::DisplayConfigResult result) { | 
|  | base::Optional<std::string> str_result = GetStringResult(result); | 
|  | if (!str_result) | 
|  | return; | 
|  | LOG(ERROR) << *str_result; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | DisplayInfoProviderChromeOS::DisplayInfoProviderChromeOS( | 
|  | service_manager::Connector* connector) | 
|  | : weak_ptr_factory_(this) { | 
|  | CHECK(connector); | 
|  | connector->BindInterface(ash::mojom::kServiceName, &cros_display_config_); | 
|  | } | 
|  |  | 
|  | DisplayInfoProviderChromeOS::~DisplayInfoProviderChromeOS() {} | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::SetDisplayProperties( | 
|  | const std::string& display_id_str, | 
|  | const api::system_display::DisplayProperties& properties, | 
|  | ErrorCallback callback) { | 
|  | base::Optional<std::string> error = | 
|  | ValidateDisplayPropertiesInput(display_id_str, properties); | 
|  | if (error) { | 
|  | RunResultCallback(std::move(callback), std::move(*error)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Process the 'isUnified' property. | 
|  | if (properties.is_unified) { | 
|  | auto layout_info = ash::mojom::DisplayLayoutInfo::New(); | 
|  | layout_info->layout_mode = *properties.is_unified | 
|  | ? ash::mojom::DisplayLayoutMode::kUnified | 
|  | : ash::mojom::DisplayLayoutMode::kNormal; | 
|  | cros_display_config_->SetDisplayLayoutInfo( | 
|  | std::move(layout_info), | 
|  | base::BindOnce( | 
|  | [](ErrorCallback callback, ash::mojom::DisplayConfigResult result) { | 
|  | std::move(callback).Run(GetStringResult(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | // Note: If other properties are set they will be ignored. | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Process the deprecated 'mirroringSourceId' property. Validation ensures | 
|  | // that no other properties are set. | 
|  | if (properties.mirroring_source_id) { | 
|  | bool mirror = !properties.mirroring_source_id->empty(); | 
|  | if (mirror) { | 
|  | // A display with the given id should exist and if should not be the same | 
|  | // as the target display's id. | 
|  |  | 
|  | int64_t mirroring_id = | 
|  | GetDisplayForId(*properties.mirroring_source_id).id(); | 
|  | if (mirroring_id == display::kInvalidDisplayId) | 
|  | RunResultCallback(std::move(callback), "Invalid mirroring source id"); | 
|  | if (mirroring_id == GetDisplayId(display_id_str)) | 
|  | RunResultCallback(std::move(callback), "Not allowed to mirror self"); | 
|  | } | 
|  | api::system_display::MirrorModeInfo info; | 
|  | info.mode = system_display::MIRROR_MODE_NORMAL; | 
|  | SetMirrorMode(info, std::move(callback)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Global config properties. | 
|  | auto config_properties = ash::mojom::DisplayConfigProperties::New(); | 
|  | config_properties->set_primary = | 
|  | properties.is_primary ? *properties.is_primary : false; | 
|  | if (properties.overscan) | 
|  | config_properties->overscan = GetInsets(*properties.overscan); | 
|  | if (properties.rotation) { | 
|  | config_properties->rotation = ash::mojom::DisplayRotation::New( | 
|  | display::Display::DegreesToRotation(*properties.rotation)); | 
|  | } | 
|  | if (properties.bounds_origin_x || properties.bounds_origin_y) { | 
|  | gfx::Point bounds_origin; | 
|  | display::Display display = GetDisplayForId(display_id_str); | 
|  | if (display.id() != display::kInvalidDisplayId) | 
|  | bounds_origin = display.bounds().origin(); | 
|  | else | 
|  | DLOG(ERROR) << "Unable to get origin for display: " << display_id_str; | 
|  | if (properties.bounds_origin_x) | 
|  | bounds_origin.set_x(*properties.bounds_origin_x); | 
|  | if (properties.bounds_origin_y) | 
|  | bounds_origin.set_y(*properties.bounds_origin_y); | 
|  | LOG(ERROR) << "Bounds origin: " << bounds_origin.ToString(); | 
|  | config_properties->bounds_origin = std::move(bounds_origin); | 
|  | } | 
|  | config_properties->display_zoom_factor = | 
|  | properties.display_zoom_factor ? *properties.display_zoom_factor : 0; | 
|  |  | 
|  | // Display mode. | 
|  | if (properties.display_mode) { | 
|  | auto mojo_display_mode = ash::mojom::DisplayMode::New(); | 
|  | const api::system_display::DisplayMode& api_display_mode = | 
|  | *properties.display_mode; | 
|  | mojo_display_mode->size = | 
|  | gfx::Size(api_display_mode.width, api_display_mode.height); | 
|  | mojo_display_mode->size_in_native_pixels = | 
|  | gfx::Size(api_display_mode.width_in_native_pixels, | 
|  | api_display_mode.height_in_native_pixels); | 
|  | mojo_display_mode->ui_scale = api_display_mode.ui_scale; | 
|  | mojo_display_mode->device_scale_factor = | 
|  | api_display_mode.device_scale_factor; | 
|  | mojo_display_mode->refresh_rate = api_display_mode.refresh_rate; | 
|  | mojo_display_mode->is_native = api_display_mode.is_native; | 
|  | config_properties->display_mode = std::move(mojo_display_mode); | 
|  | } | 
|  |  | 
|  | cros_display_config_->SetDisplayProperties( | 
|  | display_id_str, std::move(config_properties), | 
|  | base::BindOnce( | 
|  | [](ErrorCallback callback, ash::mojom::DisplayConfigResult result) { | 
|  | std::move(callback).Run(GetStringResult(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::SetDisplayLayout( | 
|  | const DisplayLayoutList& layout_list, | 
|  | ErrorCallback callback) { | 
|  | auto layout_info = ash::mojom::DisplayLayoutInfo::New(); | 
|  | // Generate the new list of layouts. | 
|  | std::vector<ash::mojom::DisplayLayoutPtr> display_layouts; | 
|  | for (const system_display::DisplayLayout& layout : layout_list) { | 
|  | auto display_layout = ash::mojom::DisplayLayout::New(); | 
|  | display_layout->id = layout.id; | 
|  | display_layout->parent_id = layout.parent_id; | 
|  | display_layout->position = GetDisplayLayoutPosition(layout.position); | 
|  | display_layout->offset = layout.offset; | 
|  | display_layouts.emplace_back(std::move(display_layout)); | 
|  | } | 
|  | layout_info->layouts = std::move(display_layouts); | 
|  | // We need to get the current layout info to provide the layout mode. | 
|  | cros_display_config_->GetDisplayLayoutInfo( | 
|  | base::BindOnce(&DisplayInfoProviderChromeOS::CallSetDisplayLayoutInfo, | 
|  | weak_ptr_factory_.GetWeakPtr(), std::move(layout_info), | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::CallSetDisplayLayoutInfo( | 
|  | ash::mojom::DisplayLayoutInfoPtr layout_info, | 
|  | ErrorCallback callback, | 
|  | ash::mojom::DisplayLayoutInfoPtr cur_info) { | 
|  | // Copy the existing layout_mode. | 
|  | layout_info->layout_mode = cur_info->layout_mode; | 
|  | cros_display_config_->SetDisplayLayoutInfo( | 
|  | std::move(layout_info), | 
|  | base::BindOnce( | 
|  | [](ErrorCallback callback, ash::mojom::DisplayConfigResult result) { | 
|  | std::move(callback).Run(GetStringResult(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::EnableUnifiedDesktop(bool enable) { | 
|  | cros_display_config_->SetUnifiedDesktopEnabled(enable); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::GetAllDisplaysInfo( | 
|  | bool single_unified, | 
|  | base::OnceCallback<void(DisplayUnitInfoList result)> callback) { | 
|  | cros_display_config_->GetDisplayLayoutInfo(base::BindOnce( | 
|  | &DisplayInfoProviderChromeOS::CallGetDisplayUnitInfoList, | 
|  | weak_ptr_factory_.GetWeakPtr(), single_unified, std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::CallGetDisplayUnitInfoList( | 
|  | bool single_unified, | 
|  | base::OnceCallback<void(DisplayUnitInfoList result)> callback, | 
|  | ash::mojom::DisplayLayoutInfoPtr layout) { | 
|  | cros_display_config_->GetDisplayUnitInfoList( | 
|  | single_unified, | 
|  | base::BindOnce( | 
|  | [](ash::mojom::DisplayLayoutInfoPtr layout, | 
|  | base::OnceCallback<void(DisplayUnitInfoList)> callback, | 
|  | std::vector<ash::mojom::DisplayUnitInfoPtr> info_list) { | 
|  | DisplayUnitInfoList all_displays; | 
|  | for (const ash::mojom::DisplayUnitInfoPtr& info : info_list) { | 
|  | system_display::DisplayUnitInfo display = | 
|  | GetDisplayUnitInfoFromMojo(*info); | 
|  | SetDisplayUnitInfoLayoutProperties(*layout, &display); | 
|  | all_displays.emplace_back(std::move(display)); | 
|  | } | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(std::move(callback), std::move(all_displays))); | 
|  | }, | 
|  | std::move(layout), std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::GetDisplayLayout( | 
|  | base::OnceCallback<void(DisplayLayoutList result)> callback) { | 
|  | cros_display_config_->GetDisplayLayoutInfo(base::BindOnce( | 
|  | [](base::OnceCallback<void(DisplayInfoProvider::DisplayLayoutList)> | 
|  | callback, | 
|  | ash::mojom::DisplayLayoutInfoPtr info) { | 
|  | DisplayInfoProvider::DisplayLayoutList result; | 
|  | if (info->layouts) { | 
|  | for (ash::mojom::DisplayLayoutPtr& layout : *info->layouts) { | 
|  | system_display::DisplayLayout display_layout; | 
|  | display_layout.id = layout->id; | 
|  | display_layout.parent_id = layout->parent_id; | 
|  | display_layout.position = | 
|  | GetLayoutPositionFromMojo(layout->position); | 
|  | display_layout.offset = layout->offset; | 
|  | result.emplace_back(std::move(display_layout)); | 
|  | } | 
|  | } | 
|  | std::move(callback).Run(std::move(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::OverscanCalibrationStart( | 
|  | const std::string& id) { | 
|  | cros_display_config_->OverscanCalibration( | 
|  | id, ash::mojom::DisplayConfigOperation::kStart, base::nullopt, | 
|  | base::BindOnce(&LogErrorResult)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::OverscanCalibrationAdjust( | 
|  | const std::string& id, | 
|  | const system_display::Insets& delta) { | 
|  | cros_display_config_->OverscanCalibration( | 
|  | id, ash::mojom::DisplayConfigOperation::kAdjust, GetInsets(delta), | 
|  | base::BindOnce(&LogErrorResult)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::OverscanCalibrationReset( | 
|  | const std::string& id) { | 
|  | cros_display_config_->OverscanCalibration( | 
|  | id, ash::mojom::DisplayConfigOperation::kReset, base::nullopt, | 
|  | base::BindOnce(&LogErrorResult)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::OverscanCalibrationComplete( | 
|  | const std::string& id) { | 
|  | cros_display_config_->OverscanCalibration( | 
|  | id, ash::mojom::DisplayConfigOperation::kComplete, base::nullopt, | 
|  | base::BindOnce(&LogErrorResult)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::ShowNativeTouchCalibration( | 
|  | const std::string& id, | 
|  | ErrorCallback callback) { | 
|  | CallTouchCalibration(id, ash::mojom::DisplayConfigOperation::kShowNative, | 
|  | nullptr, std::move(callback)); | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::StartCustomTouchCalibration( | 
|  | const std::string& id) { | 
|  | touch_calibration_target_id_ = id; | 
|  | CallTouchCalibration(id, ash::mojom::DisplayConfigOperation::kStart, nullptr, | 
|  | ErrorCallback()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::CompleteCustomTouchCalibration( | 
|  | const api::system_display::TouchCalibrationPairQuad& pairs, | 
|  | const api::system_display::Bounds& bounds) { | 
|  | auto calibration = ash::mojom::TouchCalibration::New(); | 
|  | calibration->pairs.emplace_back(GetTouchCalibrationPair(pairs.pair1)); | 
|  | calibration->pairs.emplace_back(GetTouchCalibrationPair(pairs.pair2)); | 
|  | calibration->pairs.emplace_back(GetTouchCalibrationPair(pairs.pair3)); | 
|  | calibration->pairs.emplace_back(GetTouchCalibrationPair(pairs.pair4)); | 
|  | calibration->bounds = gfx::Size(bounds.width, bounds.height); | 
|  | CallTouchCalibration(touch_calibration_target_id_, | 
|  | ash::mojom::DisplayConfigOperation::kComplete, | 
|  | std::move(calibration), ErrorCallback()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool DisplayInfoProviderChromeOS::ClearTouchCalibration(const std::string& id) { | 
|  | CallTouchCalibration(id, ash::mojom::DisplayConfigOperation::kReset, nullptr, | 
|  | ErrorCallback()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::CallTouchCalibration( | 
|  | const std::string& id, | 
|  | ash::mojom::DisplayConfigOperation op, | 
|  | ash::mojom::TouchCalibrationPtr calibration, | 
|  | ErrorCallback callback) { | 
|  | cros_display_config_->TouchCalibration( | 
|  | id, op, std::move(calibration), | 
|  | base::BindOnce( | 
|  | [](ErrorCallback callback, ash::mojom::DisplayConfigResult result) { | 
|  | if (!callback) | 
|  | return; | 
|  | std::move(callback).Run( | 
|  | result == ash::mojom::DisplayConfigResult::kSuccess | 
|  | ? base::nullopt | 
|  | : GetStringResult(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | void DisplayInfoProviderChromeOS::SetMirrorMode( | 
|  | const api::system_display::MirrorModeInfo& info, | 
|  | ErrorCallback callback) { | 
|  | auto display_layout_info = ash::mojom::DisplayLayoutInfo::New(); | 
|  | if (info.mode == api::system_display::MIRROR_MODE_OFF) { | 
|  | display_layout_info->layout_mode = ash::mojom::DisplayLayoutMode::kNormal; | 
|  | } else { | 
|  | display_layout_info->layout_mode = ash::mojom::DisplayLayoutMode::kMirrored; | 
|  | if (info.mode == api::system_display::MIRROR_MODE_MIXED) { | 
|  | if (!info.mirroring_source_id) { | 
|  | RunResultCallback(std::move(callback), "Mirror mode source id invalid"); | 
|  | return; | 
|  | } | 
|  | if (!info.mirroring_destination_ids) { | 
|  | RunResultCallback(std::move(callback), | 
|  | "Mixed mirror mode requires destination ids"); | 
|  | return; | 
|  | } | 
|  | display_layout_info->mirror_source_id = *info.mirroring_source_id; | 
|  | display_layout_info->mirror_destination_ids = | 
|  | base::make_optional<std::vector<std::string>>( | 
|  | *info.mirroring_destination_ids); | 
|  | } | 
|  | } | 
|  | cros_display_config_->SetDisplayLayoutInfo( | 
|  | std::move(display_layout_info), | 
|  | base::BindOnce( | 
|  | [](ErrorCallback callback, ash::mojom::DisplayConfigResult result) { | 
|  | std::move(callback).Run(GetStringResult(result)); | 
|  | }, | 
|  | std::move(callback))); | 
|  | } | 
|  |  | 
|  | // static | 
|  | DisplayInfoProvider* DisplayInfoProvider::Create() { | 
|  | std::unique_ptr<service_manager::Connector> connector = | 
|  | content::ServiceManagerConnection::GetForProcess() | 
|  | ->GetConnector() | 
|  | ->Clone(); | 
|  | return new DisplayInfoProviderChromeOS(connector.get()); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |