#include "content/renderer/render_thread_impl.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/heap_array.h"
#include "base/debug/leak_annotations.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/discardable_memory.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/structured_shared_memory.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/test_switches.h"
#include "base/time/time.h"
#include "cc/base/switches.h"
#include "content/app/mojo/mojo_init.h"
#include "content/common/in_process_child_thread_params.h"
#include "content/common/pseudonymization_salt.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/child_process_host_delegate.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_test_suite_base.h"
#include "content/public/test/test_content_client_initializer.h"
#include "content/public/test/test_launcher.h"
#include "content/renderer/render_process_impl.h"
#include "gpu/config/gpu_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "ui/base/ui_base_switches.h"
#ifdef LEAK_SANITIZER
#define WILL_LEAK(NAME) DISABLED_##NAME
#else
#define WILL_LEAK(NAME) NAME
#endif
namespace content {
class TestTaskCounter : public base::SingleThreadTaskRunner {
public:
TestTaskCounter() : count_(0) {}
bool PostDelayedTask(const base::Location&,
base::OnceClosure,
base::TimeDelta) override {
base::AutoLock auto_lock(lock_);
count_++;
return true;
}
bool PostNonNestableDelayedTask(const base::Location&,
base::OnceClosure,
base::TimeDelta) override {
base::AutoLock auto_lock(lock_);
count_++;
return true;
}
bool RunsTasksInCurrentSequence() const override { return true; }
int NumTasksPosted() const {
base::AutoLock auto_lock(lock_);
return count_;
}
private:
~TestTaskCounter() override {}
mutable base::Lock lock_;
int count_;
};
class RenderThreadImplBrowserTest : public testing::Test,
public ChildProcessHostDelegate {
public:
RenderThreadImplBrowserTest() {}
RenderThreadImplBrowserTest(const RenderThreadImplBrowserTest&) = delete;
RenderThreadImplBrowserTest& operator=(const RenderThreadImplBrowserTest&) =
delete;
void SetUp() override {
content_renderer_client_ = std::make_unique<ContentRendererClient>();
SetRendererClientForTesting(content_renderer_client_.get());
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner =
GetIOThreadTaskRunner({});
InitializeMojo();
process_host_ = ChildProcessHost::Create(this);
process_host_->CreateChannel();
CHECK(!process_.get());
process_ = std::make_unique<RenderProcess>();
test_task_counter_ = base::MakeRefCounted<TestTaskCounter>();
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
base::CommandLine::StringVector old_argv = cmd->argv();
cmd->AppendSwitchASCII(switches::kLang, "en-US");
cmd->AppendSwitchASCII(switches::kNumRasterThreads, "1");
cmd->AppendSwitch(switches::kIgnoreGpuBlocklist);
ContentTestSuiteBase::InitializeResourceBundle();
blink::Platform::InitializeBlink();
auto main_thread_scheduler =
blink::scheduler::CreateMockWebMainThreadSchedulerForTests();
scoped_refptr<base::SingleThreadTaskRunner> test_task_counter(
test_task_counter_.get());
base::FieldTrialList::CreateTrialsFromString(
cmd->GetSwitchValueASCII(::switches::kForceFieldTrials));
thread_ = new RenderThreadImpl(
InProcessChildThreadParams(io_task_runner,
&process_host_->GetMojoInvitation().value()),
1, std::move(main_thread_scheduler));
cmd->InitFromArgv(old_argv);
run_loop_ = std::make_unique<base::RunLoop>();
main_thread_scheduler_ =
static_cast<blink::scheduler::WebMockThreadScheduler*>(
thread_->GetWebMainThreadScheduler());
}
void TearDown() override {
SetRendererClientForTesting(nullptr);
CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSingleProcessTests));
ANNOTATE_LEAKING_OBJECT_PTR(process_.get());
process_->StopIOThreadForTesting();
process_.release();
}
const base::Process& GetProcess() override { return null_process_; }
protected:
void SetBackgroundState(base::Process::Priority process_priority) {
mojom::Renderer* renderer_interface = thread_;
const mojom::RenderProcessVisibleState visible_state =
RendererIsHidden() ? mojom::RenderProcessVisibleState::kHidden
: mojom::RenderProcessVisibleState::kVisible;
renderer_interface->SetProcessState(process_priority, visible_state);
}
void SetVisibleState(mojom::RenderProcessVisibleState visible_state) {
mojom::Renderer* renderer_interface = thread_;
const base::Process::Priority process_priority =
RendererIsBackgrounded() ? base::Process::Priority::kBestEffort
: base::Process::Priority::kUserBlocking;
renderer_interface->SetProcessState(process_priority, visible_state);
}
bool RendererIsBackgrounded() { return thread_->RendererIsBackgrounded(); }
bool RendererIsHidden() { return thread_->RendererIsHidden(); }
scoped_refptr<TestTaskCounter> test_task_counter_;
BrowserTaskEnvironment browser_threads_{
BrowserTaskEnvironment::REAL_IO_THREAD};
TestContentClientInitializer content_client_initializer_;
std::unique_ptr<ContentRendererClient> content_renderer_client_;
const base::Process null_process_;
std::unique_ptr<ChildProcessHost> process_host_;
std::unique_ptr<RenderProcess> process_;
raw_ptr<blink::scheduler::WebMockThreadScheduler> main_thread_scheduler_;
raw_ptr<RenderThreadImpl> thread_;
std::unique_ptr<base::RunLoop> run_loop_;
};
TEST_F(RenderThreadImplBrowserTest, RendererIsBackgrounded) {
SetBackgroundState(base::Process::Priority::kBestEffort);
EXPECT_TRUE(RendererIsBackgrounded());
SetBackgroundState(base::Process::Priority::kUserBlocking);
EXPECT_FALSE(RendererIsBackgrounded());
}
TEST_F(RenderThreadImplBrowserTest, RendererIsHidden) {
SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
EXPECT_TRUE(RendererIsHidden());
SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
EXPECT_FALSE(RendererIsHidden());
}
TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionVisible) {
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
SetVisibleState(mojom::RenderProcessVisibleState::kVisible);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
testing::Mock::AllowLeak(main_thread_scheduler_);
}
TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionHidden) {
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
SetVisibleState(mojom::RenderProcessVisibleState::kHidden);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
testing::Mock::AllowLeak(main_thread_scheduler_);
}
TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionBackgrounded) {
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
SetBackgroundState(base::Process::Priority::kBestEffort);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
SetBackgroundState(base::Process::Priority::kUserBlocking);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
SetBackgroundState(base::Process::Priority::kUserVisible);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
SetBackgroundState(base::Process::Priority::kBestEffort);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
testing::Mock::AllowLeak(main_thread_scheduler_);
}
TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionForegrounded) {
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
SetBackgroundState(base::Process::Priority::kUserBlocking);
testing::Mock::VerifyAndClear(main_thread_scheduler_);
testing::Mock::AllowLeak(main_thread_scheduler_);
}
TEST_F(RenderThreadImplBrowserTest, TransferSharedLastForegroundTime) {
auto time_memory = base::AtomicSharedMemory<base::TimeTicks>::Create();
ASSERT_TRUE(time_memory.has_value());
EXPECT_EQ(base::internal::GetSharedLastForegroundTimeForMetricsForTesting(),
nullptr);
thread_->TransferSharedLastForegroundTime(
time_memory->DuplicateReadOnlyRegion());
const auto* last_foreground_time_ptr =
base::internal::GetSharedLastForegroundTimeForMetricsForTesting();
EXPECT_NE(last_foreground_time_ptr, nullptr);
thread_->TransferSharedLastForegroundTime(
time_memory->DuplicateReadOnlyRegion());
EXPECT_EQ(base::internal::GetSharedLastForegroundTimeForMetricsForTesting(),
last_foreground_time_ptr);
}
}
#if BUILDFLAG(ARKWEB_TEST)
#include "arkweb/chromium_ext/content/renderer/render_thread_impl_ext_unittest.cc"
#endif