#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include <memory>
#include "base/containers/contains.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/state_transitions.h"
#include "base/supports_user_data.h"
#include "base/task/single_thread_task_runner.h"
#include "content/browser/bad_message.h"
#include "content/browser/renderer_host/agent_scheduling_group_host_factory.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/common/agent_scheduling_group.mojom.h"
#include "content/common/renderer.mojom.h"
#include "content/public/browser/render_process_host.h"
#include "ipc/constants.mojom.h"
#include "ipc/ipc_channel_factory.h"
#include "ipc/ipc_channel_proxy.h"
#if BUILDFLAG(ARKWEB_RENDER_PROCESS_MODE)
#include "third_party/ohos_ndk/includes/ohos_adapter/res_sched_client_adapter.h"
#include "arkweb/chromium_ext/base/ohos/sys_info_utils_ext.h"
#endif
#include "third_party/blink/public/mojom/shared_storage/shared_storage_worklet_service.mojom.h"
#include "third_party/blink/public/mojom/worker/worklet_global_scope_creation_params.mojom.h"
namespace content {
namespace {
using ::IPC::ChannelFactory;
using ::IPC::ChannelProxy;
using ::IPC::Listener;
using ::mojo::AssociatedReceiver;
using ::mojo::AssociatedRemote;
using ::mojo::PendingAssociatedReceiver;
using ::mojo::PendingAssociatedRemote;
using ::mojo::PendingReceiver;
using ::mojo::PendingRemote;
using ::mojo::Receiver;
using ::mojo::Remote;
static constexpr char kAgentSchedulingGroupHostDataKey[] =
"AgentSchedulingGroupHostUserDataKey";
AgentSchedulingGroupHostFactory* g_agent_scheduling_group_host_factory_ =
nullptr;
struct AgentSchedulingGroupHostUserData : public base::SupportsUserData::Data {
public:
AgentSchedulingGroupHostUserData() = default;
~AgentSchedulingGroupHostUserData() override = default;
std::set<std::unique_ptr<AgentSchedulingGroupHost>, base::UniquePtrComparator>
owned_host_set;
#if DCHECK_IS_ON()
std::set<raw_ptr<const SiteInstanceGroup, SetExperimental>>
site_instance_groups;
#endif
};
static features::MBIMode GetMBIMode() {
return base::FeatureList::IsEnabled(features::kMBIMode)
? features::kMBIModeParam.Get()
: features::MBIMode::kLegacy;
}
}
AgentSchedulingGroupHost* AgentSchedulingGroupHost::GetOrCreate(
const SiteInstanceGroup& site_instance_group,
RenderProcessHost& process) {
AgentSchedulingGroupHostUserData* data =
static_cast<AgentSchedulingGroupHostUserData*>(
process.GetUserData(kAgentSchedulingGroupHostDataKey));
if (!data) {
process.SetUserData(kAgentSchedulingGroupHostDataKey,
std::make_unique<AgentSchedulingGroupHostUserData>());
data = static_cast<AgentSchedulingGroupHostUserData*>(
process.GetUserData(kAgentSchedulingGroupHostDataKey));
}
DCHECK(data);
if (GetMBIMode() == features::MBIMode::kLegacy ||
GetMBIMode() == features::MBIMode::kEnabledPerRenderProcessHost) {
#if DCHECK_IS_ON()
DCHECK(data->site_instance_groups.empty());
#endif
if (data->owned_host_set.empty()) {
std::unique_ptr<AgentSchedulingGroupHost> host =
g_agent_scheduling_group_host_factory_
? g_agent_scheduling_group_host_factory_
->CreateAgentSchedulingGroupHost(process)
: std::make_unique<AgentSchedulingGroupHost>(process);
data->owned_host_set.insert(std::move(host));
}
DCHECK_EQ(data->owned_host_set.size(), 1ul);
return data->owned_host_set.begin()->get();
}
DCHECK_EQ(GetMBIMode(), features::MBIMode::kEnabledPerSiteInstance);
std::unique_ptr<AgentSchedulingGroupHost> host =
std::make_unique<AgentSchedulingGroupHost>(process);
AgentSchedulingGroupHost* return_host = host.get();
#if DCHECK_IS_ON()
DCHECK(!base::Contains(data->site_instance_groups, &site_instance_group));
data->site_instance_groups.insert(&site_instance_group);
#endif
data->owned_host_set.insert(std::move(host));
return return_host;
}
int32_t AgentSchedulingGroupHost::GetNextID() {
static int32_t next_id = 0;
return next_id++;
}
AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process)
: process_(process), receiver_(this) {
process_->AddObserver(this);
SetUpIPC();
implUtils = std::make_unique<AgentSchedulingGroupHostUtils>(this);
}
AgentSchedulingGroupHost::~AgentSchedulingGroupHost() {
DCHECK_EQ(state_, LifecycleState::kRenderProcessHostDestroyed);
}
void AgentSchedulingGroupHost::RenderProcessExited(
RenderProcessHost* host,
const ChildProcessTerminationInfo& info) {
SetState(LifecycleState::kRenderProcessExited);
DCHECK_EQ(host, &*process_);
ResetIPC();
if (!process_->IsDeletingSoon()) {
process_->EnableSendQueue();
SetUpIPC();
}
}
void AgentSchedulingGroupHost::RenderProcessHostDestroyed(
RenderProcessHost* host) {
if (RenderProcessHost::run_renderer_in_process()) {
if (state_ != LifecycleState::kBound) {
RenderProcessExited(host, ChildProcessTerminationInfo());
}
}
DCHECK(state_ == LifecycleState::kBound ||
state_ == LifecycleState::kRenderProcessExited);
DCHECK_EQ(host, &*process_);
process_->RemoveObserver(this);
SetState(LifecycleState::kRenderProcessHostDestroyed);
}
void AgentSchedulingGroupHost::OnBadMessageReceived() {
bad_message::ReceivedBadMessage(&*process_,
bad_message::RPH_DESERIALIZATION_FAILED);
}
void AgentSchedulingGroupHost::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
bad_message::ReceivedBadMessage(
&*process_, bad_message::ASGH_ASSOCIATED_INTERFACE_REQUEST);
}
RenderProcessHost* AgentSchedulingGroupHost::GetProcess() {
return &*process_;
}
bool AgentSchedulingGroupHost::Init() {
DCHECK(process_->GetRendererInterface());
DCHECK(mojo_remote_.is_bound());
DCHECK_EQ(state_, LifecycleState::kBound);
return process_->Init();
}
base::SafeRef<AgentSchedulingGroupHost> AgentSchedulingGroupHost::GetSafeRef()
const {
return weak_ptr_factory_.GetSafeRef();
}
ChannelProxy* AgentSchedulingGroupHost::GetChannel() {
DCHECK_EQ(state_, LifecycleState::kBound);
if (GetMBIMode() == features::MBIMode::kLegacy)
return process_->GetChannel();
DCHECK(channel_);
return channel_.get();
}
void AgentSchedulingGroupHost::AddRoute(int32_t routing_id,
Listener* listener) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(!listener_map_.Lookup(routing_id));
listener_map_.AddWithID(listener, routing_id);
process_->AddRoute(routing_id, listener);
}
void AgentSchedulingGroupHost::RemoveRoute(int32_t routing_id) {
TRACE_EVENT0("navigation", "AgentSchedulingGroupHost::RemoveRoute");
base::ScopedUmaHistogramTimer histogram_timer(
"Navigation.AgentSchedulingGroupHost.RemoveRoute");
DCHECK_EQ(state_, LifecycleState::kBound);
listener_map_.Remove(routing_id);
process_->RemoveRoute(routing_id);
}
mojom::RouteProvider* AgentSchedulingGroupHost::GetRemoteRouteProvider() {
DCHECK_EQ(state_, LifecycleState::kBound);
return remote_route_provider_.get();
}
void AgentSchedulingGroupHost::CreateFrame(mojom::CreateFrameParamsPtr params) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(process_->IsInitializedAndNotDead());
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->CreateFrame(std::move(params));
}
void AgentSchedulingGroupHost::CreateView(mojom::CreateViewParamsPtr params) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(process_->IsInitializedAndNotDead());
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->CreateView(std::move(params));
}
void AgentSchedulingGroupHost::CreateSharedStorageWorkletService(
mojo::PendingReceiver<blink::mojom::SharedStorageWorkletService> receiver,
blink::mojom::WorkletGlobalScopeCreationParamsPtr
global_scope_creation_params) {
DCHECK_EQ(state_, LifecycleState::kBound);
DCHECK(process_->IsInitializedAndNotDead());
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->CreateSharedStorageWorkletService(
std::move(receiver), std::move(global_scope_creation_params));
}
void AgentSchedulingGroupHost::
set_agent_scheduling_group_host_factory_for_testing(
AgentSchedulingGroupHostFactory* asgh_factory) {
g_agent_scheduling_group_host_factory_ = asgh_factory;
}
AgentSchedulingGroupHostFactory* AgentSchedulingGroupHost::
get_agent_scheduling_group_host_factory_for_testing() {
DCHECK(g_agent_scheduling_group_host_factory_);
return g_agent_scheduling_group_host_factory_;
}
void AgentSchedulingGroupHost::DidUnloadRenderFrame(
const blink::LocalFrameToken& frame_token) {
if (auto* frame_host = RenderFrameHostImpl::FromFrameToken(
process_->GetDeprecatedID(), frame_token)) {
frame_host->OnUnloadACK();
}
}
#if BUILDFLAG(ARKWEB_RENDER_PROCESS_MODE)
void AgentSchedulingGroupHost::ReportCreateView(int32_t process_id) {
if (implUtils) {
implUtils->ReportCreateView(process_id);
}
}
#endif
void AgentSchedulingGroupHost::ResetIPC() {
DCHECK_EQ(state_, LifecycleState::kRenderProcessExited);
receiver_.reset();
mojo_remote_.reset();
remote_route_provider_.reset();
channel_ = nullptr;
}
void AgentSchedulingGroupHost::SetUpIPC() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(state_ == LifecycleState::kNewborn ||
state_ == LifecycleState::kRenderProcessExited);
DCHECK(process_->GetRendererInterface());
DCHECK(!channel_);
DCHECK(!mojo_remote_.is_bound());
DCHECK(!receiver_.is_bound());
DCHECK(!remote_route_provider_.is_bound());
if (GetMBIMode() == features::MBIMode::kLegacy) {
process_->GetRendererInterface()->CreateAssociatedAgentSchedulingGroup(
mojo_remote_.BindNewEndpointAndPassReceiver());
} else {
auto io_task_runner = GetIOThreadTaskRunner({});
PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
process_->GetRendererInterface()->CreateAgentSchedulingGroup(
bootstrap.InitWithNewPipeAndPassReceiver());
auto channel_factory = ChannelFactory::CreateServerFactory(
bootstrap.PassPipe(), io_task_runner,
base::SingleThreadTaskRunner::GetCurrentDefault());
channel_ =
ChannelProxy::Create(std::move(channel_factory), this,
io_task_runner,
base::SingleThreadTaskRunner::GetCurrentDefault());
channel_->GetRemoteAssociatedInterface(&mojo_remote_);
}
DCHECK(mojo_remote_.is_bound());
mojo_remote_.get()->BindAssociatedInterfaces(
receiver_.BindNewEndpointAndPassRemote(),
remote_route_provider_.BindNewEndpointAndPassReceiver());
SetState(LifecycleState::kBound);
}
void AgentSchedulingGroupHost::SetState(
AgentSchedulingGroupHost::LifecycleState state) {
static const base::NoDestructor<base::StateTransitions<LifecycleState>>
transitions(base::StateTransitions<LifecycleState>({
{LifecycleState::kNewborn, {LifecycleState::kBound}},
{LifecycleState::kBound,
{LifecycleState::kRenderProcessExited,
LifecycleState::kRenderProcessHostDestroyed}},
{LifecycleState::kRenderProcessExited,
{LifecycleState::kBound,
LifecycleState::kRenderProcessHostDestroyed}},
}));
DCHECK_STATE_TRANSITION(transitions, state_, state);
state_ = state;
}
std::ostream& operator<<(std::ostream& os,
AgentSchedulingGroupHost::LifecycleState state) {
switch (state) {
case AgentSchedulingGroupHost::LifecycleState::kNewborn:
os << "Newborn";
break;
case AgentSchedulingGroupHost::LifecycleState::kBound:
os << "Bound";
break;
case AgentSchedulingGroupHost::LifecycleState::kRenderProcessExited:
os << "RenderProcessExited";
break;
case AgentSchedulingGroupHost::LifecycleState::kRenderProcessHostDestroyed:
os << "RenderProcessHostDestroyed";
break;
default:
os << "<invalid value: " << static_cast<int>(state) << ">";
}
return os;
}
Listener* AgentSchedulingGroupHost::GetListener(int32_t routing_id) {
DCHECK_NE(routing_id, IPC::mojom::kRoutingIdControl);
return listener_map_.Lookup(routing_id);
}
}