blob: f837d8763eb59150987f3097c685afa0aacd6709 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h"
#include <algorithm>
#include "base/containers/contains.h"
#include "base/metrics/field_trial_params.h"
#include "base/rand_util.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/flag_descriptions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_prefs.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/channel_info.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/variations/variations_switches.h"
#include "components/webui/flags/feature_entry.h"
#include "components/webui/flags/pref_service_flags_storage.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "ash/constants/ash_switches.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#endif
namespace {
bool force_activation_for_testing = false;
}
bool IsFeatureSupportedOnChannel(const LabInfo& lab) {
return chrome::GetChannel() <= lab.allowed_channel;
}
bool IsFeatureSupportedOnPlatform(const flags_ui::FeatureEntry* entry) {
return entry && (entry->supported_platforms &
flags_ui::FlagsState::GetCurrentPlatform()) != 0;
}
bool IsChromeLabsFeatureValid(const LabInfo& lab, Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS)
PrefService* prefs = profile->GetPrefs();
#else
PrefService* prefs = g_browser_process->local_state();
#endif
// Note: Both ChromeOS owner and non-owner use PrefServiceFlagsStorage under
// the hood. OwnersFlagsStorage has additional functionalities for setting
// flags but since we are just reading the storage assume non-owner case and
// bypass asynchronous owner check.
auto flags_storage =
std::make_unique<flags_ui::PrefServiceFlagsStorage>(prefs);
const flags_ui::FeatureEntry* entry =
about_flags::GetCurrentFlagsState()->FindFeatureEntryByName(
lab.internal_name);
return IsFeatureSupportedOnChannel(lab) &&
IsFeatureSupportedOnPlatform(entry) &&
!about_flags::ShouldSkipConditionalFeatureEntry(flags_storage.get(),
*entry);
}
void UpdateChromeLabsNewBadgePrefs(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS)
ScopedDictPrefUpdate update(
profile->GetPrefs(), chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
#else
ScopedDictPrefUpdate update(g_browser_process->local_state(),
chrome_labs_prefs::kChromeLabsNewBadgeDict);
#endif
base::Value::Dict& new_badge_prefs = update.Get();
std::vector<std::string> lab_internal_names;
const std::vector<LabInfo>& all_labs =
ChromeLabsModel::GetInstance()->GetLabInfo();
for (const auto& lab : all_labs) {
if (IsChromeLabsFeatureValid(lab, profile)) {
lab_internal_names.push_back(lab.internal_name);
if (!new_badge_prefs.Find(lab.internal_name)) {
new_badge_prefs.Set(
lab.internal_name,
chrome_labs_prefs::kChromeLabsNewExperimentPrefValue);
}
}
}
std::vector<std::string> entries_to_remove;
for (auto pref : new_badge_prefs) {
// The size of |lab_internal_names| is capped around 3-5 elements.
if (!base::Contains(lab_internal_names, pref.first)) {
entries_to_remove.push_back(pref.first);
}
}
for (const std::string& key : entries_to_remove) {
new_badge_prefs.Remove(key);
}
}
bool ShouldShowChromeLabsUI(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kSafeMode) ||
!ash::ProfileHelper::IsPrimaryProfile(profile)) {
return false;
}
#endif
return std::ranges::any_of(ChromeLabsModel::GetInstance()->GetLabInfo(),
[&profile](const LabInfo& lab) {
return IsChromeLabsFeatureValid(lab, profile);
});
}
bool AreNewChromeLabsExperimentsAvailable(Profile* profile) {
#if BUILDFLAG(IS_CHROMEOS)
ScopedDictPrefUpdate update(
profile->GetPrefs(), chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
#else
ScopedDictPrefUpdate update(g_browser_process->local_state(),
chrome_labs_prefs::kChromeLabsNewBadgeDict);
#endif
base::Value::Dict& new_badge_prefs = update.Get();
std::vector<std::string> lab_internal_names;
const std::vector<LabInfo>& all_labs =
ChromeLabsModel::GetInstance()->GetLabInfo();
return std::ranges::any_of(
all_labs.begin(), all_labs.end(), [&new_badge_prefs](const LabInfo& lab) {
std::optional<int> new_badge_pref_value =
new_badge_prefs.FindInt(lab.internal_name);
// Show the dot indicator if new experiments have not been seen yet.
return new_badge_pref_value ==
chrome_labs_prefs::kChromeLabsNewExperimentPrefValue;
});
}
bool IsChromeLabsEnabled() {
// Always early out on the stable channel regardless of other conditions.
if (chrome::GetChannel() == version_info::Channel::STABLE) {
return false;
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
variations::switches::kEnableBenchmarking)) {
return true;
}
// Could be null in unit tests.
if (!g_browser_process->local_state()) {
return false;
}
if (g_browser_process->local_state()->GetInteger(
chrome_labs_prefs::kChromeLabsActivationThreshold) ==
chrome_labs_prefs::kChromeLabsActivationThresholdDefaultValue) {
g_browser_process->local_state()->SetInteger(
chrome_labs_prefs::kChromeLabsActivationThreshold,
base::RandInt(1, 100));
}
// The percentage of users that should see the feature.
const int kChromeLabsActivationPercentage = 99;
if (force_activation_for_testing ||
g_browser_process->local_state()->GetInteger(
chrome_labs_prefs::kChromeLabsActivationThreshold) <=
kChromeLabsActivationPercentage) {
return true;
}
return false;
}
void ForceChromeLabsActivationForTesting() {
force_activation_for_testing = true;
}