[headless] Add --screen-info handling to Headless Mode on Linux
https://crrev.com/c/6021359 added --screen-info switch support to Chrome Headless Shell. This CL adds the same functionality to Chrome running in Headless mode (aka new Headless) allowing headless screen configuration and multiple screens support. Bug: 396072563 Change-Id: I044347052a656d25843556e145aea76fb4b97000 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6287167 Reviewed-by: Avi Drissman <avi@chromium.org> Commit-Queue: Peter Kvitek <kvitekp@chromium.org> Reviewed-by: Andrey Kosyakov <caseq@chromium.org> Cr-Commit-Position: refs/heads/main@{#1423544}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b1e640d046
commit
32fd08d611
chrome/browser/headless
headless/public
ui
base
ozone
platform
@ -138,6 +138,7 @@ if (!is_android) {
|
||||
data = [
|
||||
"test/data/",
|
||||
"//third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js",
|
||||
"//third_party/blink/web_tests/http/tests/inspector-protocol/resources/http-interceptor.js",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -341,4 +341,14 @@ HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
|
||||
"--ozone-override-screen-size=1234,5678")
|
||||
#endif
|
||||
|
||||
// --screen-info switch is only supported on Linux at this time.
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// This currently results in an unexpected screen orientation type,
|
||||
// see http://crbug.com/398150465.
|
||||
HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
|
||||
MultipleScreenDetails,
|
||||
"sanity/multiple-screen-details.js",
|
||||
"--screen-info={label=#1}{600x800 label=#2}")
|
||||
#endif
|
||||
|
||||
} // namespace headless
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "chrome/browser/headless/headless_mode_switches.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "ui/base/ui_base_switches.h"
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
#include "ui/gl/gl_switches.h" // nogncheck
|
||||
@ -98,7 +99,8 @@ class HeadlessModeHandleImpl : public HeadlessModeHandle {
|
||||
// Headless mode on Linux relies on ozone/headless platform.
|
||||
command_line->AppendSwitchASCII(::switches::kOzonePlatform,
|
||||
switches::kHeadless);
|
||||
if (!command_line->HasSwitch(switches::kOzoneOverrideScreenSize)) {
|
||||
if (!command_line->HasSwitch(switches::kScreenInfo) &&
|
||||
!command_line->HasSwitch(switches::kOzoneOverrideScreenSize)) {
|
||||
command_line->AppendSwitchASCII(switches::kOzoneOverrideScreenSize,
|
||||
"800,600");
|
||||
}
|
||||
|
23
chrome/browser/headless/test/data/protocol/sanity/multiple-screen-details-expected.txt
Normal file
23
chrome/browser/headless/test/data/protocol/sanity/multiple-screen-details-expected.txt
Normal file
@ -0,0 +1,23 @@
|
||||
Tests multiple screen configuration.
|
||||
Screen
|
||||
label='#1'
|
||||
0,0 800x600
|
||||
avail=0,0 800x600
|
||||
isPrimary=true
|
||||
isExtended=true
|
||||
isInternal=false
|
||||
colorDepth=24
|
||||
devicePixelRatio=1
|
||||
orientation.type=landscape-primary
|
||||
orientation.angle=0
|
||||
Screen
|
||||
label='#2'
|
||||
800,0 600x800
|
||||
avail=800,0 600x800
|
||||
isPrimary=false
|
||||
isExtended=true
|
||||
isInternal=false
|
||||
colorDepth=24
|
||||
devicePixelRatio=1
|
||||
orientation.type=landscape-primary
|
||||
orientation.angle=0
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
(async function(testRunner) {
|
||||
const {session, dp} =
|
||||
await testRunner.startBlank(`Tests multiple screen configuration.`);
|
||||
|
||||
const HttpInterceptor =
|
||||
await testRunner.loadScriptAbsolute('../resources/http-interceptor.js');
|
||||
const httpInterceptor = await (new HttpInterceptor(testRunner, dp)).init();
|
||||
|
||||
httpInterceptor.setDisableRequestedUrlsLogging(true);
|
||||
httpInterceptor.addResponse(
|
||||
'https://example.com/index.html',
|
||||
'<html><head><link rel="icon" href="data:,"></head></html>');
|
||||
|
||||
await dp.Browser.grantPermissions({permissions: ['windowManagement']});
|
||||
|
||||
await session.navigate('https://example.com/index.html');
|
||||
|
||||
const result = await session.evaluateAsync(async () => {
|
||||
const screenDetails = await getScreenDetails();
|
||||
const screenDetailed = screenDetails.screens.map(s => {
|
||||
const lines = [
|
||||
`Screen`,
|
||||
` label='${s.label}'`,
|
||||
` ${s.left},${s.top} ${s.width}x${s.height}`,
|
||||
` avail=${s.availLeft},${s.availTop} ${s.availWidth}x${s.availHeight}`,
|
||||
` isPrimary=${s.isPrimary}`,
|
||||
` isExtended=${s.isExtended}`,
|
||||
` isInternal=${s.isInternal}`,
|
||||
` colorDepth=${s.colorDepth}`,
|
||||
` devicePixelRatio=${s.devicePixelRatio}`,
|
||||
` orientation.type=${s.orientation.type}`,
|
||||
` orientation.angle=${s.orientation.angle}`,
|
||||
];
|
||||
return lines.join('\n');
|
||||
});
|
||||
return screenDetailed.join('\n');
|
||||
});
|
||||
|
||||
testRunner.log(result);
|
||||
|
||||
testRunner.completeTest();
|
||||
})
|
@ -114,8 +114,8 @@ inline constexpr char kProxyBypassList[] = "proxy-bypass-list";
|
||||
// affects HTTP and HTTPS requests.
|
||||
inline constexpr char kProxyServer[] = "proxy-server";
|
||||
|
||||
// Headless screen info in the format: {0,0 800x600},{800,0 600x800}.
|
||||
// See //headless/lib/browser/headless_screen_info.h for more details.
|
||||
// Headless screen info in the format: {0,0 800x600}{800,0 600x800}.
|
||||
// See //components/headless/screen_info/headless_screen_info.h for details.
|
||||
inline constexpr char kScreenInfo[] = "screen-info";
|
||||
|
||||
// A string used to override the default user agent with a custom one.
|
||||
|
@ -106,4 +106,8 @@ const char kUIDisablePartialSwap[] = "ui-disable-partial-swap";
|
||||
// Enables the ozone x11 clipboard for linux-chromeos.
|
||||
const char kUseSystemClipboard[] = "use-system-clipboard";
|
||||
|
||||
// Headless screen info in the format: {0,0 800x600}{800,0 600x800}.
|
||||
// See //components/headless/screen_info/headless_screen_info.h for details.
|
||||
const char kScreenInfo[] = "screen-info";
|
||||
|
||||
} // namespace switches
|
||||
|
@ -48,6 +48,9 @@ COMPONENT_EXPORT(UI_BASE) extern const char kTopChromeTouchUiEnabled[];
|
||||
COMPONENT_EXPORT(UI_BASE) extern const char kUIDisablePartialSwap[];
|
||||
COMPONENT_EXPORT(UI_BASE) extern const char kUseSystemClipboard[];
|
||||
|
||||
// Headless related.
|
||||
COMPONENT_EXPORT(UI_BASE) extern const char kScreenInfo[];
|
||||
|
||||
// Test related.
|
||||
COMPONENT_EXPORT(UI_BASE) extern const char kDisallowNonExactResourceReuse[];
|
||||
COMPONENT_EXPORT(UI_BASE) extern const char kMangleLocalizedStrings[];
|
||||
|
@ -27,6 +27,7 @@ source_set("headless") {
|
||||
deps = [
|
||||
"//base",
|
||||
"//build:chromecast_buildflags",
|
||||
"//components/headless/screen_info",
|
||||
"//gpu/vulkan:buildflags",
|
||||
"//skia",
|
||||
"//ui/base",
|
||||
|
@ -1,3 +1,4 @@
|
||||
include_rules = [
|
||||
"+third_party/skia/include",
|
||||
"+components/headless/screen_info",
|
||||
]
|
||||
|
@ -7,17 +7,32 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check_deref.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/not_fatal_until.h"
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "components/headless/screen_info/headless_screen_info.h"
|
||||
#include "ui/base/ui_base_switches.h"
|
||||
#include "ui/display/display_finder.h"
|
||||
#include "ui/display/util/display_util.h"
|
||||
#include "ui/ozone/public/ozone_switches.h"
|
||||
|
||||
using headless::HeadlessScreenInfo;
|
||||
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
|
||||
// By default headless screen has 1x1 size and 1.0 scale factor. Headless
|
||||
// screen size can be overridden using --ozone-override-screen-size switch.
|
||||
//
|
||||
// More complex headless screen configuration (including multiple screens)
|
||||
// can be specified using the --screen-info command line switch.
|
||||
// See //components/headless/screen_info/headless_screen_info.h for details.
|
||||
|
||||
// Ozone/headless display defaults.
|
||||
constexpr int64_t kHeadlessDisplayId = 1;
|
||||
constexpr int64_t kHeadlessDisplayIdBase = 1;
|
||||
constexpr float kHeadlessDisplayScale = 1.0f;
|
||||
constexpr gfx::Size kHeadlessDisplaySize(1, 1);
|
||||
|
||||
@ -36,11 +51,11 @@ bool ParseScreenSize(const std::string& screen_size, int* width, int* height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
gfx::Rect GetDisplayBounds() {
|
||||
gfx::Rect GetHeadlessDisplayBounds() {
|
||||
gfx::Rect bounds(kHeadlessDisplaySize);
|
||||
|
||||
const base::CommandLine& command_line =
|
||||
*base::CommandLine::ForCurrentProcess();
|
||||
CHECK_DEREF(base::CommandLine::ForCurrentProcess());
|
||||
if (command_line.HasSwitch(switches::kOzoneOverrideScreenSize)) {
|
||||
int width, height;
|
||||
std::string screen_size =
|
||||
@ -53,12 +68,60 @@ gfx::Rect GetDisplayBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
std::vector<HeadlessScreenInfo> GetScreenInfo() {
|
||||
std::vector<HeadlessScreenInfo> screen_info;
|
||||
|
||||
const base::CommandLine& command_line =
|
||||
CHECK_DEREF(base::CommandLine::ForCurrentProcess());
|
||||
if (command_line.HasSwitch(switches::kScreenInfo)) {
|
||||
const std::string switch_value =
|
||||
command_line.GetSwitchValueASCII(switches::kScreenInfo);
|
||||
auto screen_info_or_error = HeadlessScreenInfo::FromString(switch_value);
|
||||
CHECK(screen_info_or_error.has_value()) << screen_info_or_error.error();
|
||||
screen_info = screen_info_or_error.value();
|
||||
} else {
|
||||
screen_info.push_back(
|
||||
HeadlessScreenInfo({.bounds = GetHeadlessDisplayBounds(),
|
||||
.device_pixel_ratio = kHeadlessDisplayScale}));
|
||||
}
|
||||
return screen_info;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HeadlessScreen::HeadlessScreen() {
|
||||
display::Display display(kHeadlessDisplayId);
|
||||
display.SetScaleAndBounds(kHeadlessDisplayScale, GetDisplayBounds());
|
||||
display_list_.AddDisplay(display, display::DisplayList::Type::PRIMARY);
|
||||
std::vector<HeadlessScreenInfo> screen_info = GetScreenInfo();
|
||||
|
||||
base::flat_set<int64_t> internal_display_ids;
|
||||
display::DisplayList::Type type = display::DisplayList::Type::PRIMARY;
|
||||
for (const auto& it : screen_info) {
|
||||
static int64_t synthesized_display_id = kHeadlessDisplayIdBase;
|
||||
display::Display display(synthesized_display_id++);
|
||||
display.set_label(it.label);
|
||||
display.set_color_depth(it.color_depth);
|
||||
display.SetScaleAndBounds(it.device_pixel_ratio, it.bounds);
|
||||
|
||||
if (!it.work_area_insets.IsEmpty()) {
|
||||
display.UpdateWorkAreaFromInsets(it.work_area_insets);
|
||||
}
|
||||
|
||||
if (it.rotation) {
|
||||
CHECK(display::Display::IsValidRotation(it.rotation));
|
||||
display.SetRotationAsDegree(it.rotation);
|
||||
}
|
||||
|
||||
if (it.is_internal) {
|
||||
internal_display_ids.insert(display.id());
|
||||
}
|
||||
|
||||
is_natural_landscape_map_.insert({display.id(), display.is_landscape()});
|
||||
|
||||
display_list_.AddDisplay(display, type);
|
||||
|
||||
type = display::DisplayList::Type::NOT_PRIMARY;
|
||||
}
|
||||
|
||||
display::SetInternalDisplayIds(std::move(internal_display_ids));
|
||||
}
|
||||
|
||||
HeadlessScreen::~HeadlessScreen() = default;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "ui/display/display_list.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/ozone/public/platform_screen.h"
|
||||
@ -38,6 +39,7 @@ class HeadlessScreen : public PlatformScreen {
|
||||
void RemoveObserver(display::DisplayObserver* observer) override;
|
||||
private:
|
||||
display::DisplayList display_list_;
|
||||
base::flat_map<int64_t, bool> is_natural_landscape_map_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
Reference in New Issue
Block a user