
NOTREACHED() and NOTREACHED_IN_MIGRATION() are both CHECK-fatal now. The former is [[noreturn]] so this CL also performs dead-code removal after the NOTREACHED(). This CL does not attempt to do additional rewrites of any surrounding code, like: if (!foo) { NOTREACHED(); } to CHECK(foo); Those transforms take a non-trivial amount of time (and there are thousands of instances). Cleanup can be left as an exercise for the reader. This does clean up kCrashOnDanglingBrowserContext as both paths of the kill switch are currently fatal. This has been rolled out for a long time. Bug: 40580068, 40062641 Change-Id: Ib88e710d003e2e48df3fc502ca54d2341d157a0e Cq-Include-Trybots: luci.chromium.try:linux-dcheck-off-rel Low-Coverage-Reason: OTHER Should-be-unreachable code Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5974816 Reviewed-by: Łukasz Anforowicz <lukasza@chromium.org> Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org> Reviewed-by: Rakina Zata Amni <rakina@chromium.org> Auto-Submit: Peter Boström <pbos@chromium.org> Reviewed-by: Sam McNally <sammc@chromium.org> Cr-Commit-Position: refs/heads/main@{#1376522}
159 lines
5.8 KiB
C++
159 lines
5.8 KiB
C++
// Copyright 2012 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/field_trial_synchronizer.h"
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/metrics/field_trial.h"
|
|
#include "base/metrics/field_trial_list_including_low_anonymity.h"
|
|
#include "base/strings/strcat.h"
|
|
#include "base/threading/thread.h"
|
|
#include "components/metrics/persistent_system_profile.h"
|
|
#include "components/variations/active_field_trials.h"
|
|
#include "components/variations/variations_client.h"
|
|
#include "content/common/renderer_variations_configuration.mojom.h"
|
|
#include "content/public/browser/browser_context.h"
|
|
#include "content/public/browser/browser_task_traits.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/render_process_host.h"
|
|
#include "ipc/ipc_channel_proxy.h"
|
|
#include "mojo/public/cpp/bindings/associated_remote.h"
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
FieldTrialSynchronizer* g_instance = nullptr;
|
|
|
|
// Notifies all renderer processes about the |group_name| that is finalized for
|
|
// the given field trail (|field_trial_name|). This is called on UI thread.
|
|
void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
|
|
const std::string& group_name,
|
|
bool is_low_anonymity,
|
|
bool is_overridden) {
|
|
// To iterate over RenderProcessHosts, or to send messages to the hosts, we
|
|
// need to be on the UI thread.
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
|
// Low anonymity or overridden field trials must not be written to persistent
|
|
// data, otherwise they might end up being logged in metrics.
|
|
//
|
|
// TODO(crbug.com/40263398): split this out into a separate class that
|
|
// registers using |FieldTrialList::AddObserver()| (and so doesn't get told
|
|
// about low anonymity trials at all).
|
|
if (!is_low_anonymity) {
|
|
// Note this in the persistent profile as it will take a while for a new
|
|
// "complete" profile to be generated.
|
|
metrics::GlobalPersistentSystemProfile::GetInstance()->AddFieldTrial(
|
|
field_trial_name,
|
|
is_overridden ? base::StrCat({group_name, variations::kOverrideSuffix})
|
|
: group_name);
|
|
}
|
|
|
|
for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
|
|
!it.IsAtEnd(); it.Advance()) {
|
|
auto* host = it.GetCurrentValue();
|
|
IPC::ChannelProxy* channel = host->GetChannel();
|
|
// channel might be null in tests.
|
|
if (host->IsInitializedAndNotDead() && channel) {
|
|
mojo::AssociatedRemote<mojom::RendererVariationsConfiguration>
|
|
renderer_variations_configuration;
|
|
channel->GetRemoteAssociatedInterface(&renderer_variations_configuration);
|
|
renderer_variations_configuration->SetFieldTrialGroup(field_trial_name,
|
|
group_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
void FieldTrialSynchronizer::CreateInstance() {
|
|
// Only 1 instance is allowed per process.
|
|
DCHECK(!g_instance);
|
|
g_instance = new FieldTrialSynchronizer();
|
|
}
|
|
|
|
FieldTrialSynchronizer::FieldTrialSynchronizer() {
|
|
// TODO(crbug.com/40263398): consider whether there is a need to exclude low
|
|
// anonymity field trials from non-browser processes (or to plumb through the
|
|
// anonymity property for more fine-grained access).
|
|
bool success = base::FieldTrialListIncludingLowAnonymity::AddObserver(this);
|
|
// Ensure the observer was actually registered.
|
|
DCHECK(success);
|
|
|
|
variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
|
|
NotifyAllRenderersOfVariationsHeader();
|
|
}
|
|
|
|
void FieldTrialSynchronizer::OnFieldTrialGroupFinalized(
|
|
const base::FieldTrial& trial,
|
|
const std::string& group_name) {
|
|
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
|
NotifyAllRenderersOfFieldTrial(trial.trial_name(), group_name,
|
|
trial.is_low_anonymity(),
|
|
trial.IsOverridden());
|
|
} else {
|
|
// Note that in some tests, `trial` may not be alive when the posted task is
|
|
// called.
|
|
GetUIThreadTaskRunner({})->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&NotifyAllRenderersOfFieldTrial, trial.trial_name(),
|
|
group_name, trial.is_low_anonymity(),
|
|
trial.IsOverridden()));
|
|
}
|
|
}
|
|
|
|
// static
|
|
void FieldTrialSynchronizer::NotifyAllRenderersOfVariationsHeader() {
|
|
// To iterate over RenderProcessHosts, or to send messages to the hosts, we
|
|
// need to be on the UI thread.
|
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
|
|
!it.IsAtEnd(); it.Advance()) {
|
|
UpdateRendererVariationsHeader(it.GetCurrentValue());
|
|
}
|
|
}
|
|
|
|
// static
|
|
void FieldTrialSynchronizer::UpdateRendererVariationsHeader(
|
|
RenderProcessHost* host) {
|
|
if (!host->IsInitializedAndNotDead())
|
|
return;
|
|
|
|
IPC::ChannelProxy* channel = host->GetChannel();
|
|
|
|
// |channel| might be null in tests.
|
|
if (!channel)
|
|
return;
|
|
|
|
variations::VariationsClient* client =
|
|
host->GetBrowserContext()->GetVariationsClient();
|
|
|
|
// |client| might be null in tests.
|
|
if (!client || client->IsOffTheRecord())
|
|
return;
|
|
|
|
mojo::AssociatedRemote<mojom::RendererVariationsConfiguration>
|
|
renderer_variations_configuration;
|
|
channel->GetRemoteAssociatedInterface(&renderer_variations_configuration);
|
|
renderer_variations_configuration->SetVariationsHeaders(
|
|
client->GetVariationsHeaders());
|
|
}
|
|
|
|
void FieldTrialSynchronizer::VariationIdsHeaderUpdated() {
|
|
// PostTask to avoid recursive lock.
|
|
GetUIThreadTaskRunner({})->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(
|
|
&FieldTrialSynchronizer::NotifyAllRenderersOfVariationsHeader));
|
|
}
|
|
|
|
FieldTrialSynchronizer::~FieldTrialSynchronizer() {
|
|
NOTREACHED();
|
|
}
|
|
|
|
} // namespace content
|