blob: 5bfded55b28f91643f5941d8824dc84bc24de91c [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2012 The Chromium Authors
[email protected]63a8ba12011-04-29 05:42:222// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Clarke9fc39b9b2020-01-31 17:29:475#include "content/browser/field_trial_synchronizer.h"
[email protected]63a8ba12011-04-29 05:42:226
Hans Wennborg0917de892020-04-28 20:21:157#include "base/check_op.h"
Avi Drissmanadac21992023-01-11 23:46:398#include "base/functional/bind.h"
James Lee01bb2e92023-04-15 19:46:409#include "base/metrics/field_trial.h"
10#include "base/metrics/field_trial_list_including_low_anonymity.h"
Dan Harrington42d5c832023-11-27 18:10:1611#include "base/strings/strcat.h"
[email protected]63a8ba12011-04-29 05:42:2212#include "base/threading/thread.h"
bcwhitec1ddec32017-06-29 14:13:0413#include "components/metrics/persistent_system_profile.h"
Dan Harrington42d5c832023-11-27 18:10:1614#include "components/variations/active_field_trials.h"
Alex Clarke3ebd9772020-02-28 10:50:2715#include "components/variations/variations_client.h"
Alex Clarke9fc39b9b2020-01-31 17:29:4716#include "content/common/renderer_variations_configuration.mojom.h"
Alex Clarke3ebd9772020-02-28 10:50:2717#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2818#include "content/public/browser/browser_task_traits.h"
[email protected]c38831a12011-10-28 12:44:4919#include "content/public/browser/browser_thread.h"
[email protected]f3b1a082011-11-18 00:34:3020#include "content/public/browser/render_process_host.h"
Lei Zhang487cb3cf2022-02-28 01:11:5921#include "ipc/ipc_channel_proxy.h"
Julie Jeongeun Kim68fde7a2019-09-30 10:39:3822#include "mojo/public/cpp/bindings/associated_remote.h"
[email protected]63a8ba12011-04-29 05:42:2223
Alex Clarke9fc39b9b2020-01-31 17:29:4724namespace content {
[email protected]631bb742011-11-02 11:29:3925
Xi Han095f39342019-08-08 20:00:5426namespace {
27
Francois Dorayda23db52021-05-06 22:34:3628FieldTrialSynchronizer* g_instance = nullptr;
Francois Doray9985b632021-05-05 14:45:3729
Francois Dorayda23db52021-05-06 22:34:3630// Notifies all renderer processes about the |group_name| that is finalized for
31// the given field trail (|field_trial_name|). This is called on UI thread.
32void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
Dan Harrington42d5c832023-11-27 18:10:1633 const std::string& group_name,
34 bool is_low_anonymity,
35 bool is_overridden) {
Francois Doray9985b632021-05-05 14:45:3736 // To iterate over RenderProcessHosts, or to send messages to the hosts, we
37 // need to be on the UI thread.
38 DCHECK_CURRENTLY_ON(BrowserThread::UI);
39
Dan Harrington42d5c832023-11-27 18:10:1640 // Low anonymity or overridden field trials must not be written to persistent
41 // data, otherwise they might end up being logged in metrics.
James Lee01bb2e92023-04-15 19:46:4042 //
Alison Gale770f3fc2024-04-27 00:39:5843 // TODO(crbug.com/40263398): split this out into a separate class that
James Lee01bb2e92023-04-15 19:46:4044 // registers using |FieldTrialList::AddObserver()| (and so doesn't get told
45 // about low anonymity trials at all).
Dan Harrington42d5c832023-11-27 18:10:1646 if (!is_low_anonymity) {
James Lee01bb2e92023-04-15 19:46:4047 // Note this in the persistent profile as it will take a while for a new
48 // "complete" profile to be generated.
49 metrics::GlobalPersistentSystemProfile::GetInstance()->AddFieldTrial(
Dan Harrington42d5c832023-11-27 18:10:1650 field_trial_name,
51 is_overridden ? base::StrCat({group_name, variations::kOverrideSuffix})
52 : group_name);
James Lee01bb2e92023-04-15 19:46:4053 }
bcwhitec1ddec32017-06-29 14:13:0454
Alex Clarke9fc39b9b2020-01-31 17:29:4755 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
[email protected]63a8ba12011-04-29 05:42:2256 !it.IsAtEnd(); it.Advance()) {
Alexander Yashkin69b731c2019-01-17 10:14:1557 auto* host = it.GetCurrentValue();
58 IPC::ChannelProxy* channel = host->GetChannel();
nigeltaof007b702017-02-04 00:53:2559 // channel might be null in tests.
Alexander Yashkin69b731c2019-01-17 10:14:1560 if (host->IsInitializedAndNotDead() && channel) {
Alex Clarke9fc39b9b2020-01-31 17:29:4761 mojo::AssociatedRemote<mojom::RendererVariationsConfiguration>
62 renderer_variations_configuration;
63 channel->GetRemoteAssociatedInterface(&renderer_variations_configuration);
64 renderer_variations_configuration->SetFieldTrialGroup(field_trial_name,
65 group_name);
nigeltaof007b702017-02-04 00:53:2566 }
[email protected]63a8ba12011-04-29 05:42:2267 }
68}
69
Francois Dorayda23db52021-05-06 22:34:3670} // namespace
71
72// static
73void FieldTrialSynchronizer::CreateInstance() {
74 // Only 1 instance is allowed per process.
75 DCHECK(!g_instance);
76 g_instance = new FieldTrialSynchronizer();
77}
78
79FieldTrialSynchronizer::FieldTrialSynchronizer() {
Alison Gale770f3fc2024-04-27 00:39:5880 // TODO(crbug.com/40263398): consider whether there is a need to exclude low
James Lee01bb2e92023-04-15 19:46:4081 // anonymity field trials from non-browser processes (or to plumb through the
82 // anonymity property for more fine-grained access).
83 bool success = base::FieldTrialListIncludingLowAnonymity::AddObserver(this);
Francois Dorayda23db52021-05-06 22:34:3684 // Ensure the observer was actually registered.
85 DCHECK(success);
86
87 variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
88 NotifyAllRenderersOfVariationsHeader();
89}
90
[email protected]63a8ba12011-04-29 05:42:2291void FieldTrialSynchronizer::OnFieldTrialGroupFinalized(
Dan Harrington42d5c832023-11-27 18:10:1692 const base::FieldTrial& trial,
[email protected]63a8ba12011-04-29 05:42:2293 const std::string& group_name) {
Matt Falkenhagen439e1a32021-09-24 10:30:1694 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Dan Harrington42d5c832023-11-27 18:10:1695 NotifyAllRenderersOfFieldTrial(trial.trial_name(), group_name,
96 trial.is_low_anonymity(),
97 trial.IsOverridden());
Matt Falkenhagen439e1a32021-09-24 10:30:1698 } else {
Dan Harrington42d5c832023-11-27 18:10:1699 // Note that in some tests, `trial` may not be alive when the posted task is
100 // called.
Matt Falkenhagen439e1a32021-09-24 10:30:16101 GetUIThreadTaskRunner({})->PostTask(
Dan Harrington42d5c832023-11-27 18:10:16102 FROM_HERE,
103 base::BindOnce(&NotifyAllRenderersOfFieldTrial, trial.trial_name(),
104 group_name, trial.is_low_anonymity(),
105 trial.IsOverridden()));
Matt Falkenhagen439e1a32021-09-24 10:30:16106 }
Alex Clarke3ebd9772020-02-28 10:50:27107}
108
109// static
110void FieldTrialSynchronizer::NotifyAllRenderersOfVariationsHeader() {
111 // To iterate over RenderProcessHosts, or to send messages to the hosts, we
112 // need to be on the UI thread.
113 DCHECK_CURRENTLY_ON(BrowserThread::UI);
114 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
115 !it.IsAtEnd(); it.Advance()) {
116 UpdateRendererVariationsHeader(it.GetCurrentValue());
117 }
118}
119
120// static
121void FieldTrialSynchronizer::UpdateRendererVariationsHeader(
122 RenderProcessHost* host) {
123 if (!host->IsInitializedAndNotDead())
124 return;
125
126 IPC::ChannelProxy* channel = host->GetChannel();
127
128 // |channel| might be null in tests.
129 if (!channel)
130 return;
131
132 variations::VariationsClient* client =
133 host->GetBrowserContext()->GetVariationsClient();
134
135 // |client| might be null in tests.
Ramin Halavati15a10212020-05-18 05:26:13136 if (!client || client->IsOffTheRecord())
Alex Clarke3ebd9772020-02-28 10:50:27137 return;
138
139 mojo::AssociatedRemote<mojom::RendererVariationsConfiguration>
140 renderer_variations_configuration;
141 channel->GetRemoteAssociatedInterface(&renderer_variations_configuration);
Caitlin Fischerb7568772020-09-23 14:32:00142 renderer_variations_configuration->SetVariationsHeaders(
143 client->GetVariationsHeaders());
Alex Clarke3ebd9772020-02-28 10:50:27144}
145
146void FieldTrialSynchronizer::VariationIdsHeaderUpdated() {
147 // PostTask to avoid recursive lock.
Gabriel Charettec40dd9c2021-03-30 11:00:11148 GetUIThreadTaskRunner({})->PostTask(
149 FROM_HERE,
Alex Clarke3ebd9772020-02-28 10:50:27150 base::BindOnce(
151 &FieldTrialSynchronizer::NotifyAllRenderersOfVariationsHeader));
[email protected]63a8ba12011-04-29 05:42:22152}
153
[email protected]649d1c02012-04-27 02:56:21154FieldTrialSynchronizer::~FieldTrialSynchronizer() {
Peter Boströmfc7ddc182024-10-31 19:37:21155 NOTREACHED();
[email protected]649d1c02012-04-27 02:56:21156}
Alex Clarke9fc39b9b2020-01-31 17:29:47157
158} // namespace content