blob: ceb189c9a6291a45d74e8d82701f6790a5199bc0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/variations/active_field_trials.h"
#include <stddef.h>
#include <algorithm>
#include <string_view>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/metrics/field_trial.h"
#include "base/no_destructor.h"
#include "base/process/launch.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/variations/hashing.h"
#include "components/variations/synthetic_trials_active_group_id_provider.h"
#include "components/variations/variations_crash_keys.h"
#include "components/variations/variations_switches.h"
namespace variations {
namespace {
std::string& GetSeedVersionInternal() {
static base::NoDestructor<std::string> seed_version;
return *seed_version;
}
void AppendActiveGroupIdsAsStrings(
const std::vector<ActiveGroupId> name_group_ids,
std::vector<std::string>* output) {
for (const auto& active_group_id : name_group_ids) {
output->push_back(base::StringPrintf("%x-%x", active_group_id.name,
active_group_id.group));
}
}
uint32_t HashNameAndSuffix(std::string_view base_name,
std::string_view optional_suffix) {
// Note that most of the time, suffixes are empty, so this avoids creating new
// strings if not necessary.
if (optional_suffix.empty()) {
return HashName(base_name);
}
return HashName(base::StrCat({base_name, optional_suffix}));
}
uint32_t HashNameAndSuffix(std::string_view base_name,
std::string_view optional_suffix,
std::string_view optional_suffix2) {
if (optional_suffix.empty() && optional_suffix2.empty()) {
return HashName(base_name);
}
return HashName(base::StrCat({base_name, optional_suffix, optional_suffix2}));
}
ActiveGroupId MakeActiveGroupIdWithSuffix(std::string_view trial_name,
std::string_view group_name,
std::string_view optional_suffix,
bool is_overridden) {
ActiveGroupId id;
id.name = HashNameAndSuffix(trial_name, optional_suffix);
id.group =
HashNameAndSuffix(group_name, optional_suffix,
is_overridden ? kOverrideSuffix : std::string_view());
return id;
}
} // namespace
ActiveGroupId MakeActiveGroupId(std::string_view trial_name,
std::string_view group_name,
bool is_overridden) {
ActiveGroupId id;
id.name = HashName(trial_name);
id.group = !is_overridden ? HashName(group_name)
: HashNameAndSuffix(group_name, kOverrideSuffix);
return id;
}
void GetFieldTrialActiveGroupIdsForActiveGroups(
std::string_view suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
for (const auto& active_group : active_groups) {
ActiveGroupId group_id = MakeActiveGroupIdWithSuffix(
active_group.trial_name, active_group.group_name, suffix,
active_group.is_overridden);
name_group_ids->push_back(std::move(group_id));
}
}
void GetFieldTrialActiveGroupIds(std::string_view suffix,
std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
// A note on thread safety: Since GetActiveFieldTrialGroups() is thread
// safe, and we operate on a separate list of that data, this function is
// technically thread safe as well, with respect to the FieldTrialList data.
base::FieldTrial::ActiveGroups active_groups;
base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
name_group_ids);
}
void GetFieldTrialActiveGroupIds(
std::string_view suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids) {
DCHECK(name_group_ids->empty());
GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
name_group_ids);
}
void GetFieldTrialActiveGroupIdsAsStrings(std::string_view suffix,
std::vector<std::string>* output) {
DCHECK(output->empty());
std::vector<ActiveGroupId> name_group_ids;
GetFieldTrialActiveGroupIds(suffix, &name_group_ids);
AppendActiveGroupIdsAsStrings(name_group_ids, output);
}
void GetFieldTrialActiveGroupIdsAsStrings(
std::string_view suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<std::string>* output) {
DCHECK(output->empty());
std::vector<ActiveGroupId> name_group_ids;
GetFieldTrialActiveGroupIds(suffix, active_groups, &name_group_ids);
AppendActiveGroupIdsAsStrings(name_group_ids, output);
}
void GetSyntheticTrialGroupIdsAsString(std::vector<std::string>* output) {
std::vector<ActiveGroupId> name_group_ids =
SyntheticTrialsActiveGroupIdProvider::GetInstance()->GetActiveGroupIds();
AppendActiveGroupIdsAsStrings(name_group_ids, output);
}
bool HasSyntheticTrial(std::string_view trial_name) {
std::vector<std::string> synthetic_trials;
variations::GetSyntheticTrialGroupIdsAsString(&synthetic_trials);
std::string trial_hash = variations::HashNameAsHexString(trial_name);
return std::ranges::any_of(synthetic_trials, [&trial_hash](
const auto& trial) {
return base::StartsWith(trial, trial_hash, base::CompareCase::SENSITIVE);
});
}
bool IsInSyntheticTrialGroup(std::string_view trial_name,
std::string_view trial_group) {
std::vector<std::string> synthetic_trials;
GetSyntheticTrialGroupIdsAsString(&synthetic_trials);
return base::Contains(
synthetic_trials,
base::StringPrintf("%x-%x", HashName(trial_name), HashName(trial_group)));
}
void SetSeedVersion(const std::string& seed_version) {
GetSeedVersionInternal() = seed_version;
SetVariationsSeedVersionCrashKey(seed_version);
}
const std::string& GetSeedVersion() {
return GetSeedVersionInternal();
}
#if BUILDFLAG(USE_BLINK)
void PopulateLaunchOptionsWithVariationsInfo(
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
base::GlobalDescriptors::Key descriptor_key,
base::ScopedFD& descriptor_to_share,
#endif
base::CommandLine* command_line,
base::LaunchOptions* launch_options) {
base::FieldTrialList::PopulateLaunchOptionsWithFieldTrialState(
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
descriptor_key, descriptor_to_share,
#endif
command_line, launch_options);
command_line->AppendSwitchASCII(switches::kVariationsSeedVersion,
GetSeedVersion());
}
#endif // !BUILDFLAG(USE_BLINK)
namespace test {
void TestGetFieldTrialActiveGroupIds(
std::string_view suffix,
const base::FieldTrial::ActiveGroups& active_groups,
std::vector<ActiveGroupId>* name_group_ids) {
GetFieldTrialActiveGroupIdsForActiveGroups(suffix, active_groups,
name_group_ids);
}
} // namespace test
} // namespace variations