// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/browser_child_process_backgrounded_bridge.h"
#include "base/mac/mac_util.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "gpu/config/gpu_finch_features.h"
namespace content {
base::Process::Priority GetProcessPriority(base::ProcessId pid) {
base::Process process = base::Process::Open(pid);
if (process.is_current()) {
base::SelfPortProvider self_port_provider;
return process.GetPriority(&self_port_provider);
}
return process.GetPriority(
content::BrowserChildProcessHost::GetPortProvider());
}
void SetProcessPriority(base::ProcessId pid, base::Process::Priority priority) {
base::Process process = base::Process::Open(pid);
if (process.is_current()) {
base::SelfPortProvider self_port_provider;
process.SetPriority(&self_port_provider, priority);
return;
}
process.SetPriority(content::BrowserChildProcessHost::GetPortProvider(),
priority);
}
class BrowserChildProcessBackgroundedBridgeTest
: public content::ContentBrowserTest,
public base::PortProvider::Observer,
public testing::WithParamInterface<bool> {
public:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
features::kAdjustGpuProcessPriority);
content::BrowserChildProcessBackgroundedBridge::
SetOSNotificationsEnabledForTesting(false);
content::ContentBrowserTest::SetUp();
}
void TearDown() override {
content::ContentBrowserTest::TearDown();
content::BrowserChildProcessBackgroundedBridge::
SetOSNotificationsEnabledForTesting(true);
}
// Waits until the port for the GPU process is available.
void WaitForPort() {
auto* port_provider = content::BrowserChildProcessHost::GetPortProvider();
// Note: On macOS, a process id and a process handle are the same thing.
DCHECK(port_provider->TaskForHandle(
content::GpuProcessHost::Get()->process_id()) == MACH_PORT_NULL);
port_provider->AddObserver(this);
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
void EnsureBackgroundedStateChange() {
// Do a round-trip to the process launcher task to ensure any queued task is
// run.
base::RunLoop run_loop;
GetProcessLauncherTaskRunner()->PostTaskAndReply(
FROM_HERE, base::DoNothing(), run_loop.QuitClosure());
run_loop.Run();
}
private:
void OnReceivedTaskPort(base::ProcessHandle process_handle) override {
if (process_handle != content::GpuProcessHost::Get()->process_id()) {
return;
}
content::BrowserChildProcessHost::GetPortProvider()->RemoveObserver(this);
std::move(quit_closure_).Run();
}
base::test::ScopedFeatureList scoped_feature_list_;
base::OnceClosure quit_closure_;
};
IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest,
InitiallyForegrounded) {
if (base::mac::MacOSMajorVersion() >= 13) {
GTEST_SKIP() << "Flaking on macOS 13: https://crbug.com/1444130";
}
// Set the browser process as foregrounded.
SetProcessPriority(base::Process::Current().Pid(),
base::Process::Priority::kUserBlocking);
// Wait until we receive the port for the GPU process.
WaitForPort();
// Ensure that the initial backgrounded state changed.
EnsureBackgroundedStateChange();
auto* gpu_process_host = content::GpuProcessHost::Get();
EXPECT_TRUE(gpu_process_host);
EXPECT_EQ(GetProcessPriority(gpu_process_host->process_id()),
base::Process::Priority::kUserBlocking);
}
// TODO(crbug.com/40899195): Disabled because this test is flaky.
IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest,
DISABLED_InitiallyBackgrounded) {
// Set the browser process as backgrounded.
SetProcessPriority(base::Process::Current().Pid(),
base::Process::Priority::kBestEffort);
// Wait until we receive the port for the GPU process.
WaitForPort();
// Ensure that the initial backgrounded state changed.
EnsureBackgroundedStateChange();
auto* gpu_process_host = content::GpuProcessHost::Get();
EXPECT_TRUE(gpu_process_host);
EXPECT_EQ(GetProcessPriority(gpu_process_host->process_id()),
base::Process::Priority::kUserVisible);
}
// Flaky: https://crbug.com/1443367
IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest,
DISABLED_OnBackgroundedStateChanged) {
// Wait until we receive the port for the GPU process.
WaitForPort();
auto* gpu_process_host = content::GpuProcessHost::Get();
EXPECT_TRUE(gpu_process_host);
auto* bridge =
gpu_process_host->browser_child_process_backgrounded_bridge_for_testing();
ASSERT_TRUE(bridge);
bridge->SimulateBrowserProcessForegroundedForTesting();
EnsureBackgroundedStateChange();
EXPECT_EQ(GetProcessPriority(gpu_process_host->process_id()),
base::Process::Priority::kUserBlocking);
bridge->SimulateBrowserProcessBackgroundedForTesting();
EnsureBackgroundedStateChange();
EXPECT_EQ(GetProcessPriority(gpu_process_host->process_id()),
base::Process::Priority::kUserVisible);
bridge->SimulateBrowserProcessForegroundedForTesting();
EnsureBackgroundedStateChange();
EXPECT_EQ(GetProcessPriority(gpu_process_host->process_id()),
base::Process::Priority::kUserBlocking);
}
} // namespace content