blob: 1d7eec45d41650f00156f40e73f5c0cd0fd7e54f [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill_assistant/browser/script_parameters.h"
#include <array>
#include <sstream>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/autofill_assistant/browser/assistant_field_trial_util.h"
#include "components/autofill_assistant/browser/public/public_script_parameters.h"
#include "components/autofill_assistant/browser/user_data.h"
#include "components/autofill_assistant/browser/value_util.h"
namespace autofill_assistant {
namespace {
// Prefix used to annotate values coming from the the startup parameters.
const char kParameterMemoryPrefix[] = "param:";
// Converts a value to a target type. Returns nullopt for invalid or
// non-existent values. Expects bool parameters as 'false' and 'true'.
template <typename T>
absl::optional<T> GetTypedParameter(
const base::flat_map<std::string, ValueProto> parameters,
const std::string& key) {
auto iter = parameters.find(key);
if (iter == parameters.end())
return absl::nullopt;
std::string value = iter->second.strings().values(0);
std::stringstream ss;
ss << value;
T out;
if (!(ss >> std::boolalpha >> out)) {
LOG(ERROR) << "Error trying to convert parameter '" << key
<< "' with value '" << value << "' to target type";
return absl::nullopt;
}
return out;
}
} // namespace
// Parameter that allows setting the color of the overlay.
const char kOverlayColorParameterName[] = "OVERLAY_COLORS";
// Special parameter for instructing the client to request and run a trigger
// script from a remote RPC prior to starting the regular flow.
const char kRequestTriggerScriptParameterName[] = "REQUEST_TRIGGER_SCRIPT";
// The parameter key for the user's email, as indicated by the caller.
const char kCallerEmailParameterName[] = "USER_EMAIL";
// Special parameter for declaring a user to be in a trigger script experiment.
const char kTriggerScriptExperimentParameterName[] =
"TRIGGER_SCRIPT_EXPERIMENT";
// Parameter that allows enabling Text-to-Speech functionality.
const char kEnableTtsParameterName[] = "ENABLE_TTS";
// Allows enabling observer-based WaitForDOM.
const char kEnableObserversParameter[] = "ENABLE_OBSERVER_WAIT_FOR_DOM";
// Parameter to specify experiments.
const char kExperimentsParameterName[] = "EXPERIMENT_IDS";
// Parameter to disable CUP RPC signing. Intended for internal use only.
const char kDisableRpcSigningParamaterName[] = "DISABLE_RPC_SIGNING";
// Parameter to send the annotate DOM model version. Should only be used if we
// expect the model to be used.
const char kSendAnnotateDomModelVersion[] = "SEND_ANNOTATE_DOM_MODEL_VERSION";
// Whether this script does not requires a round trip.
const char kIsNoRoundTrip[] = "IS_NO_ROUND_TRIP";
// The list of non sensitive script parameters that client requests are allowed
// to send to the backend i.e., they do not require explicit approval in the
// autofill-assistant onboarding. Even so, please always reach out to Chrome
// privacy when you plan to make use of this list, and/or adjust it.
constexpr std::array<const char*, 7> kNonSensitiveScriptParameters = {
public_script_parameters::kDebugBundleIdParameterName,
"DEBUG_BUNDLE_VERSION",
public_script_parameters::kDebugSocketIdParameterName,
"FALLBACK_BUNDLE_ID",
"FALLBACK_BUNDLE_VERSION",
public_script_parameters::kIntentParameterName,
"CAPABILITIES_REQUEST_ID"};
// Parameters to specify details before the first backend roundtrip.
const char kDetailsShowInitialParameterName[] = "DETAILS_SHOW_INITIAL";
const char kDetailsTitleParameterName[] = "DETAILS_TITLE";
const char kDetailsDescriptionLine1ParameterName[] =
"DETAILS_DESCRIPTION_LINE_1";
const char kDetailsDescriptionLine2ParameterName[] =
"DETAILS_DESCRIPTION_LINE_2";
const char kDetailsDescriptionLine3ParameterName[] =
"DETAILS_DESCRIPTION_LINE_3";
const char kDetailsImageUrl[] = "DETAILS_IMAGE_URL";
const char kDetailsImageAccessibilityHint[] =
"DETAILS_IMAGE_ACCESSIBILITY_HINT";
const char kDetailsImageClickthroughUrl[] = "DETAILS_IMAGE_CLICKTHROUGH_URL";
const char kDetailsTotalPriceLabel[] = "DETAILS_TOTAL_PRICE_LABEL";
const char kDetailsTotalPrice[] = "DETAILS_TOTAL_PRICE";
const char kRunHeadless[] = "RUN_HEADLESS";
const char kFieldTrialPrefix[] = "FIELD_TRIAL_";
const char kUseAssistantUi[] = "USE_ASSISTANT_UI";
ScriptParameters::ScriptParameters(
const base::flat_map<std::string, std::string>& parameters) {
for (const auto& it : parameters) {
parameters_.emplace(
it.first, SimpleValue(it.second, /* is_client_side_only= */ false));
}
}
ScriptParameters::ScriptParameters() = default;
ScriptParameters::~ScriptParameters() = default;
ScriptParameters& ScriptParameters::operator=(ScriptParameters&&) = default;
void ScriptParameters::MergeWith(const ScriptParameters& another) {
for (const auto& param : another.parameters_) {
parameters_.insert(param);
}
}
bool ScriptParameters::Matches(const ScriptParameterMatchProto& proto) const {
auto opt_value = GetParameter(proto.name());
if (!proto.exists()) {
return !opt_value;
}
if (!proto.has_value_equals()) {
return opt_value.has_value();
}
return opt_value && proto.value_equals() == opt_value.value();
}
google::protobuf::RepeatedPtrField<ScriptParameterProto>
ScriptParameters::ToProto(bool only_non_sensitive_allowlisted) const {
google::protobuf::RepeatedPtrField<ScriptParameterProto> out;
if (only_non_sensitive_allowlisted) {
for (const char* key : kNonSensitiveScriptParameters) {
auto iter = parameters_.find(key);
if (iter == parameters_.end()) {
continue;
}
auto* out_param = out.Add();
out_param->set_name(key);
out_param->set_value(iter->second.strings().values(0));
}
return out;
}
// TODO(arbesser): Send properly typed parameters to backend.
for (const auto& parameter : parameters_) {
if (parameter.first == public_script_parameters::kEnabledParameterName) {
continue;
}
if (parameter.second.is_client_side_only()) {
continue;
}
auto* out_param = out.Add();
out_param->set_name(parameter.first);
out_param->set_value(parameter.second.strings().values(0));
}
return out;
}
absl::optional<std::string> ScriptParameters::GetParameter(
const std::string& name) const {
auto iter = parameters_.find(name);
if (iter == parameters_.end())
return absl::nullopt;
return iter->second.strings().values(0);
}
bool ScriptParameters::HasExperimentId(const std::string& experiment_id) const {
return base::ranges::count(GetExperiments(), experiment_id) > 0;
}
absl::optional<std::string> ScriptParameters::GetOverlayColors() const {
return GetParameter(kOverlayColorParameterName);
}
absl::optional<std::string> ScriptParameters::GetPasswordChangeUsername()
const {
return GetParameter(
public_script_parameters::kPasswordChangeUsernameParameterName);
}
absl::optional<bool> ScriptParameters::GetRequestsTriggerScript() const {
return GetTypedParameter<bool>(parameters_,
kRequestTriggerScriptParameterName);
}
absl::optional<bool> ScriptParameters::GetStartImmediately() const {
return GetTypedParameter<bool>(
parameters_, public_script_parameters::kStartImmediatelyParameterName);
}
absl::optional<bool> ScriptParameters::GetEnabled() const {
return GetTypedParameter<bool>(
parameters_, public_script_parameters::kEnabledParameterName);
}
absl::optional<std::string> ScriptParameters::GetOriginalDeeplink() const {
return GetParameter(public_script_parameters::kOriginalDeeplinkParameterName);
}
absl::optional<bool> ScriptParameters::GetTriggerScriptExperiment() const {
return GetTypedParameter<bool>(parameters_,
kTriggerScriptExperimentParameterName);
}
absl::optional<std::string> ScriptParameters::GetIntent() const {
return GetParameter(public_script_parameters::kIntentParameterName);
}
absl::optional<std::string> ScriptParameters::GetCallerEmail() const {
return GetParameter(kCallerEmailParameterName);
}
absl::optional<bool> ScriptParameters::GetEnableTts() const {
return GetTypedParameter<bool>(parameters_, kEnableTtsParameterName);
}
absl::optional<bool> ScriptParameters::GetEnableObserverWaitForDom() const {
return GetTypedParameter<bool>(parameters_, kEnableObserversParameter);
}
absl::optional<int> ScriptParameters::GetCaller() const {
return GetTypedParameter<int>(parameters_,
public_script_parameters::kCallerParameterName);
}
absl::optional<int> ScriptParameters::GetSource() const {
return GetTypedParameter<int>(parameters_,
public_script_parameters::kSourceParameterName);
}
std::vector<std::string> ScriptParameters::GetExperiments() const {
absl::optional<std::string> experiments_str =
GetParameter(kExperimentsParameterName);
if (!experiments_str) {
return std::vector<std::string>();
}
return base::SplitString(*experiments_str, ",",
base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
}
absl::optional<bool> ScriptParameters::GetDisableRpcSigning() const {
return GetTypedParameter<bool>(parameters_, kDisableRpcSigningParamaterName);
}
absl::optional<bool> ScriptParameters::GetSendAnnotateDomModelVersion() const {
return GetTypedParameter<bool>(parameters_, kSendAnnotateDomModelVersion);
}
bool ScriptParameters::GetRunHeadless() const {
return GetTypedParameter<bool>(parameters_, kRunHeadless).value_or(false);
}
bool ScriptParameters::GetUseAssistantUi() const {
return GetTypedParameter<bool>(parameters_, kUseAssistantUi).value_or(false);
}
absl::optional<std::string> ScriptParameters::GetFieldTrialGroup(
const int field_trial_slot) const {
DCHECK_GE(field_trial_slot, 1);
DCHECK_LE(field_trial_slot,
AssistantFieldTrialUtil::kSyntheticTrialParamCount);
return GetTypedParameter<std::string>(
parameters_, base::StrCat({kFieldTrialPrefix,
base::NumberToString(field_trial_slot)}));
}
absl::optional<bool> ScriptParameters::GetDetailsShowInitial() const {
return GetTypedParameter<bool>(parameters_, kDetailsShowInitialParameterName);
}
absl::optional<std::string> ScriptParameters::GetDetailsTitle() const {
return GetParameter(kDetailsTitleParameterName);
}
absl::optional<std::string> ScriptParameters::GetDetailsDescriptionLine1()
const {
return GetParameter(kDetailsDescriptionLine1ParameterName);
}
absl::optional<std::string> ScriptParameters::GetDetailsDescriptionLine2()
const {
return GetParameter(kDetailsDescriptionLine2ParameterName);
}
absl::optional<std::string> ScriptParameters::GetDetailsDescriptionLine3()
const {
return GetParameter(kDetailsDescriptionLine3ParameterName);
}
absl::optional<std::string> ScriptParameters::GetDetailsImageUrl() const {
return GetParameter(kDetailsImageUrl);
}
absl::optional<std::string> ScriptParameters::GetDetailsImageAccessibilityHint()
const {
return GetParameter(kDetailsImageAccessibilityHint);
}
absl::optional<std::string> ScriptParameters::GetDetailsImageClickthroughUrl()
const {
return GetParameter(kDetailsImageClickthroughUrl);
}
absl::optional<std::string> ScriptParameters::GetDetailsTotalPriceLabel()
const {
return GetParameter(kDetailsTotalPriceLabel);
}
absl::optional<std::string> ScriptParameters::GetDetailsTotalPrice() const {
return GetParameter(kDetailsTotalPrice);
}
absl::optional<bool> ScriptParameters::GetIsNoRoundtrip() const {
return GetTypedParameter<bool>(parameters_, kIsNoRoundTrip);
}
void ScriptParameters::UpdateDeviceOnlyParameters(
const base::flat_map<std::string, std::string>& parameters) {
for (const auto& parameter : parameters) {
parameters_[parameter.first] =
SimpleValue(parameter.second, /* is_client_side_only= */ true);
}
}
void ScriptParameters::WriteToUserData(UserData* user_data) const {
for (const auto& parameter : parameters_) {
user_data->SetAdditionalValue(kParameterMemoryPrefix + parameter.first,
parameter.second);
}
}
} // namespace autofill_assistant