blob: a3d7072a89acecec93174c601ef3c6ede196d436 [file] [log] [blame]
// 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/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/extensions/system_display/display_info_provider.h"
#include "chrome/browser/ui/ash/tablet_mode_client.h"
#include "content/public/browser/system_connector.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.device_scale_factor = mode.device_scale_factor;
result.refresh_rate = mode.refresh_rate;
result.is_native = mode.is_native;
result.is_interlaced = std::make_unique<bool>(mode.is_interlaced);
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() = default;
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->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;
mojo_display_mode->is_interlaced =
api_display_mode.is_interlaced && *(api_display_mode.is_interlaced);
config_properties->display_mode = std::move(mojo_display_mode);
}
cros_display_config_->SetDisplayProperties(
display_id_str, std::move(config_properties),
ash::mojom::DisplayConfigSource::kUser,
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)));
}
void DisplayInfoProviderChromeOS::StartObserving() {
DisplayInfoProvider::StartObserving();
TabletModeClient* client = TabletModeClient::Get();
if (client)
client->AddObserver(this);
}
void DisplayInfoProviderChromeOS::StopObserving() {
DisplayInfoProvider::StopObserving();
TabletModeClient* client = TabletModeClient::Get();
if (client)
client->RemoveObserver(this);
}
void DisplayInfoProviderChromeOS::OnTabletModeToggled(bool enabled) {
DispatchOnDisplayChangedEvent();
}
std::unique_ptr<DisplayInfoProvider> CreateChromeDisplayInfoProvider() {
return std::make_unique<DisplayInfoProviderChromeOS>(
content::GetSystemConnector());
}
} // namespace extensions