blob: 29b5c433aabfc613874eec1fdeb008bad35e73fb [file] [log] [blame]
// Copyright 2019 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/webui/flags/flags_ui_handler.h"
#include "base/functional/bind.h"
#include "build/build_config.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/common/channel_info.h"
#include "components/version_info/channel.h"
#include "components/webui/flags/flags_storage.h"
#include "components/webui/flags/flags_ui_constants.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/settings/about_flags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#endif
namespace {
bool ExtractKeyValue(const base::Value::List& args,
std::string& key,
std::string& value) {
if (args.size() != 2) {
return false;
}
if (!args[0].is_string() || !args[1].is_string()) {
return false;
}
key = args[0].GetString();
value = args[1].GetString();
return !key.empty();
}
} // namespace
FlagsUIHandler::FlagsUIHandler()
: access_(flags_ui::kGeneralAccessFlagsOnly),
experimental_features_callback_id_(""),
deprecated_features_callback_id_("") {}
FlagsUIHandler::~FlagsUIHandler() = default;
void FlagsUIHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"requestDeprecatedFeatures",
base::BindRepeating(&FlagsUIHandler::HandleRequestDeprecatedFeatures,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kRequestExperimentalFeatures,
base::BindRepeating(&FlagsUIHandler::HandleRequestExperimentalFeatures,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kEnableExperimentalFeature,
base::BindRepeating(
&FlagsUIHandler::HandleEnableExperimentalFeatureMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kSetOriginListFlag,
base::BindRepeating(&FlagsUIHandler::HandleSetOriginListFlagMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kSetStringFlag,
base::BindRepeating(&FlagsUIHandler::HandleSetStringFlagMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kRestartBrowser,
base::BindRepeating(&FlagsUIHandler::HandleRestartBrowser,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
flags_ui::kResetAllFlags,
base::BindRepeating(&FlagsUIHandler::HandleResetAllFlags,
base::Unretained(this)));
}
void FlagsUIHandler::Init(std::unique_ptr<flags_ui::FlagsStorage> flags_storage,
flags_ui::FlagAccess access) {
flags_storage_ = std::move(flags_storage);
access_ = access;
if (!experimental_features_callback_id_.empty()) {
SendExperimentalFeatures(/*deprecated_features_only=*/false);
}
if (!deprecated_features_callback_id_.empty()) {
SendExperimentalFeatures(/*deprecated_features_only=*/true);
}
}
void FlagsUIHandler::HandleRequestDeprecatedFeatures(
const base::Value::List& args) {
AllowJavascript();
const base::Value& callback_id = args[0];
deprecated_features_callback_id_ = callback_id.GetString();
// Bail out if the handler hasn't been initialized yet. The request will be
// handled after the initialization.
if (!flags_storage_) {
return;
}
SendExperimentalFeatures(/*deprecated_features_only=*/true);
}
void FlagsUIHandler::HandleRequestExperimentalFeatures(
const base::Value::List& args) {
AllowJavascript();
const base::Value& callback_id = args[0];
experimental_features_callback_id_ = callback_id.GetString();
// Bail out if the handler hasn't been initialized yet. The request will be
// handled after the initialization.
if (!flags_storage_) {
return;
}
SendExperimentalFeatures(/*deprecated_features_only=*/false);
}
void FlagsUIHandler::SendExperimentalFeatures(bool deprecated_features_only) {
base::Value::Dict results;
base::Value::List supported_features;
base::Value::List unsupported_features;
if (deprecated_features_only) {
about_flags::GetFlagFeatureEntriesForDeprecatedPage(
flags_storage_.get(), access_, supported_features,
unsupported_features);
} else {
about_flags::GetFlagFeatureEntries(flags_storage_.get(), access_,
supported_features,
unsupported_features);
}
results.Set(flags_ui::kSupportedFeatures, std::move(supported_features));
results.Set(flags_ui::kUnsupportedFeatures, std::move(unsupported_features));
results.Set(flags_ui::kNeedsRestart,
about_flags::IsRestartNeededToCommitChanges());
results.Set(flags_ui::kShowOwnerWarning,
access_ == flags_ui::kGeneralAccessFlagsOnly);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
version_info::Channel channel = chrome::GetChannel();
results.Set(
flags_ui::kShowBetaChannelPromotion,
channel == version_info::Channel::STABLE && !deprecated_features_only);
results.Set(
flags_ui::kShowDevChannelPromotion,
channel == version_info::Channel::BETA && !deprecated_features_only);
#else
results.Set(flags_ui::kShowBetaChannelPromotion, false);
results.Set(flags_ui::kShowDevChannelPromotion, false);
#endif
if (deprecated_features_only) {
ResolveJavascriptCallback(base::Value(deprecated_features_callback_id_),
results);
deprecated_features_callback_id_.clear();
} else {
ResolveJavascriptCallback(base::Value(experimental_features_callback_id_),
results);
experimental_features_callback_id_.clear();
}
}
void FlagsUIHandler::HandleEnableExperimentalFeatureMessage(
const base::Value::List& args) {
DCHECK(flags_storage_);
DCHECK_EQ(2u, args.size());
if (args.size() != 2) {
return;
}
if (!args[0].is_string() || !args[1].is_string()) {
NOTREACHED();
}
const std::string& entry_internal_name = args[0].GetString();
const std::string& enable_str = args[1].GetString();
if (entry_internal_name.empty()) {
NOTREACHED();
}
about_flags::SetFeatureEntryEnabled(flags_storage_.get(), entry_internal_name,
enable_str == "true");
}
void FlagsUIHandler::HandleSetOriginListFlagMessage(
const base::Value::List& args) {
DCHECK(flags_storage_);
std::string entry_internal_name, value_str;
if (!ExtractKeyValue(args, entry_internal_name, value_str)) {
NOTREACHED();
}
about_flags::SetOriginListFlag(entry_internal_name, value_str,
flags_storage_.get());
}
void FlagsUIHandler::HandleSetStringFlagMessage(const base::Value::List& args) {
DCHECK(flags_storage_);
std::string entry_internal_name, value_str;
if (!ExtractKeyValue(args, entry_internal_name, value_str)) {
NOTREACHED();
}
about_flags::SetStringFlag(entry_internal_name, value_str,
flags_storage_.get());
}
void FlagsUIHandler::HandleRestartBrowser(const base::Value::List& args) {
DCHECK(flags_storage_);
#if BUILDFLAG(IS_CHROMEOS)
// On Chrome OS be less intrusive and restart inside the user session after
// we apply the newly selected flags.
VLOG(1) << "Restarting to apply per-session flags...";
ash::about_flags::FeatureFlagsUpdate(*flags_storage_,
Profile::FromWebUI(web_ui())->GetPrefs())
.UpdateSessionManager();
#endif
chrome::AttemptRestart();
}
void FlagsUIHandler::HandleResetAllFlags(const base::Value::List& args) {
DCHECK(flags_storage_);
about_flags::ResetAllFlags(flags_storage_.get());
}