blob: 8b7aa45763064ebc36dbc6053bec42c06fb85eef [file] [log] [blame]
// 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/public/browser/synthetic_trial_syncer.h"
#include "base/functional/bind.h"
#include "components/variations/synthetic_trial_registry.h"
#include "content/common/synthetic_trial_configuration.mojom.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_process_host.h"
namespace content {
namespace {
std::vector<mojom::SyntheticTrialGroupPtr> ConvertTrialGroupsToMojo(
const std::vector<variations::SyntheticTrialGroup>& trials) {
std::vector<mojom::SyntheticTrialGroupPtr> groups;
for (const auto& trial : trials) {
std::string trial_name(trial.trial_name());
std::string group_name(trial.group_name());
groups.push_back(mojom::SyntheticTrialGroup::New(trial_name, group_name));
}
return groups;
}
void NotifyChildProcess(
mojo::Remote<mojom::SyntheticTrialConfiguration>&
synthetic_trial_configuration,
const std::vector<variations::SyntheticTrialGroup>& trials_updated,
const std::vector<variations::SyntheticTrialGroup>& trials_removed) {
if (trials_updated.size() > 0) {
synthetic_trial_configuration->AddOrUpdateSyntheticTrialGroups(
ConvertTrialGroupsToMojo(trials_updated));
}
if (trials_removed.size() > 0) {
synthetic_trial_configuration->RemoveSyntheticTrialGroups(
ConvertTrialGroupsToMojo(trials_removed));
}
}
ChildProcessHost* FindChildProcessHost(int unique_id) {
for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
if (iter.GetData().id == unique_id) {
return iter.GetHost();
}
}
return nullptr;
}
} // namespace
std::unique_ptr<SyntheticTrialSyncer> SyntheticTrialSyncer::Create(
variations::SyntheticTrialRegistry* registry) {
// Only 1 instance is allowed for the browser process.
static bool s_called = false;
CHECK(!s_called);
std::unique_ptr<SyntheticTrialSyncer> instance =
std::make_unique<SyntheticTrialSyncer>(registry);
registry->AddObserver(instance.get());
BrowserChildProcessObserver::Add(instance.get());
s_called = true;
return instance;
}
void SyntheticTrialSyncer::OnDisconnected(int unique_child_process_id) {
child_process_unique_id_to_mojo_connections_.erase(unique_child_process_id);
}
SyntheticTrialSyncer::SyntheticTrialSyncer(
variations::SyntheticTrialRegistry* registry)
: registry_(registry) {}
SyntheticTrialSyncer::~SyntheticTrialSyncer() {
registry_->RemoveObserver(this);
BrowserChildProcessObserver::Remove(this);
for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
!it.IsAtEnd(); it.Advance()) {
if (RenderProcessHost* host = it.GetCurrentValue()) {
host->RemoveObserver(this);
}
}
}
void SyntheticTrialSyncer::OnSyntheticTrialsChanged(
const std::vector<variations::SyntheticTrialGroup>& trials_updated,
const std::vector<variations::SyntheticTrialGroup>& trials_removed,
const std::vector<variations::SyntheticTrialGroup>& groups) {
for (auto& it : child_process_unique_id_to_mojo_connections_) {
NotifyChildProcess(it.second, trials_updated, trials_removed);
}
}
void SyntheticTrialSyncer::BrowserChildProcessLaunchedAndConnected(
const ChildProcessData& data) {
const int unique_id = data.id;
ChildProcessHost* host = FindChildProcessHost(unique_id);
if (host == nullptr) {
return;
}
mojo::Remote<mojom::SyntheticTrialConfiguration>
synthetic_trial_configuration;
host->BindReceiver(
synthetic_trial_configuration.BindNewPipeAndPassReceiver());
// SyntheticTrialSyncer will be destroyed at PostMainMessageLoopRun() while
// BrowserMainRunner's shutdown. So the disconnect handler task will not
// be invoked after the destruction.
synthetic_trial_configuration.set_disconnect_handler(
base::BindOnce(&SyntheticTrialSyncer::OnDisconnected,
base::Unretained(this), unique_id));
NotifyChildProcess(synthetic_trial_configuration,
registry_->GetSyntheticTrialGroups(), {});
// Keep the mojo connection not to repeatedly create and destroy the child
// process synthetic trial syncer.
child_process_unique_id_to_mojo_connections_[unique_id] =
std::move(synthetic_trial_configuration);
}
void SyntheticTrialSyncer::BrowserChildProcessHostDisconnected(
const ChildProcessData& data) {
// Since data.GetProcess().IsValid() returns false, we cannot get the child
// process' pid here.
child_process_unique_id_to_mojo_connections_.erase(data.id);
}
void SyntheticTrialSyncer::OnRenderProcessHostCreated(RenderProcessHost* host) {
host->AddObserver(this);
}
void SyntheticTrialSyncer::RenderProcessReady(RenderProcessHost* host) {
const base::Process& process = host->GetProcess();
if (!process.IsValid()) {
return;
}
const int unique_id = host->GetDeprecatedID();
mojo::Remote<mojom::SyntheticTrialConfiguration>
synthetic_trial_configuration;
host->BindReceiver(
synthetic_trial_configuration.BindNewPipeAndPassReceiver());
// SyntheticTrialSyncer will be destroyed at PostMainMessageLoopRun() while
// BrowserMainRunner's shutdown. So the disconnect handler task will not
// be invoked after the destruction.
synthetic_trial_configuration.set_disconnect_handler(
base::BindOnce(&SyntheticTrialSyncer::OnDisconnected,
base::Unretained(this), unique_id));
NotifyChildProcess(synthetic_trial_configuration,
registry_->GetSyntheticTrialGroups(), {});
// Keep the mojo connection not to repeatedly create and destroy the child
// process synthetic trial syncer.
child_process_unique_id_to_mojo_connections_[unique_id] =
std::move(synthetic_trial_configuration);
}
void SyntheticTrialSyncer::RenderProcessExited(
RenderProcessHost* host,
const ChildProcessTerminationInfo& info) {
child_process_unique_id_to_mojo_connections_.erase(host->GetDeprecatedID());
// To ensure this is removed from the observer list, call RemoveObserver()
// again.
host->RemoveObserver(this);
}
void SyntheticTrialSyncer::RenderProcessHostDestroyed(RenderProcessHost* host) {
child_process_unique_id_to_mojo_connections_.erase(host->GetDeprecatedID());
// To ensure this is removed from the observer list, call RemoveObserver()
// again.
host->RemoveObserver(this);
}
} // namespace content