blob: 5dad3b63d02e9ba55531207a2d1cbbc69482a773 [file] [log] [blame]
// Copyright 2018 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/actions/use_address_action.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/actions/fallback_handler/required_field.h"
#include "components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/field_formatter.h"
#include "components/autofill_assistant/browser/user_data_util.h"
#include "components/autofill_assistant/browser/user_model.h"
#include "components/autofill_assistant/browser/value_util.h"
namespace autofill_assistant {
UseAddressAction::UseAddressAction(ActionDelegate* delegate,
const ActionProto& proto)
: Action(delegate, proto) {
DCHECK(proto.has_use_address());
selector_ = Selector(proto.use_address().form_field_element());
}
UseAddressAction::~UseAddressAction() = default;
void UseAddressAction::InternalProcessAction(
ProcessActionCallback action_callback) {
process_action_callback_ = std::move(action_callback);
if (selector_.empty() && !proto_.use_address().skip_autofill()) {
VLOG(1) << "UseAddress failed: |selector| empty";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
if (proto_.use_address().skip_autofill() &&
proto_.use_address().required_fields().empty()) {
VLOG(1) << "UseAddress failed: |skip_autofill| without required fields";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
// Ensure data already selected in a previous action.
switch (proto_.use_address().address_source_case()) {
case UseAddressProto::kName: {
if (proto_.use_address().name().empty()) {
VLOG(1) << "UseAddress failed: |name| specified but empty";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
auto* profile = delegate_->GetUserData()->selected_address(
proto_.use_address().name());
if (profile == nullptr) {
auto* error_info = processed_action_proto_->mutable_status_details()
->mutable_autofill_error_info();
error_info->set_address_key_requested(proto_.use_address().name());
error_info->set_client_memory_address_key_names(
delegate_->GetUserData()->GetAllAddressKeyNames());
error_info->set_address_pointee_was_null(true);
VLOG(1) << "UseAddress failed: no profile found under "
<< proto_.use_address().name();
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
profile_ = MakeUniqueFromProfile(*profile);
break;
}
case UseAddressProto::kModelIdentifier: {
if (proto_.use_address().model_identifier().empty()) {
VLOG(1) << "UseAddress failed: |model_identifier| set but empty";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
auto profile_value = delegate_->GetUserModel()->GetValue(
proto_.use_address().model_identifier());
if (!profile_value.has_value()) {
VLOG(1) << "UseAddress failed: "
<< proto_.use_address().model_identifier()
<< " not found in user model";
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
if (profile_value->profiles().values().size() != 1) {
VLOG(1) << "UseAddress failed: expected single profile for "
<< proto_.use_address().model_identifier() << ", but got "
<< *profile_value;
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
auto* profile = delegate_->GetUserModel()->GetProfile(
profile_value->profiles().values(0).guid());
if (profile == nullptr) {
VLOG(1) << "UseAddress failed: profile not found for guid "
<< *profile_value;
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
profile_ = MakeUniqueFromProfile(*profile);
break;
}
case UseAddressProto::ADDRESS_SOURCE_NOT_SET:
EndAction(ClientStatus(INVALID_ACTION));
return;
}
DCHECK(profile_ != nullptr);
FillFormWithData();
}
void UseAddressAction::EndAction(const ClientStatus& status) {
if (fallback_handler_)
action_stopwatch_.TransferToWaitTime(fallback_handler_->TotalWaitTime());
UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
void UseAddressAction::FillFormWithData() {
if (selector_.empty()) {
DCHECK(proto_.use_address().skip_autofill());
OnWaitForElement(OkClientStatus());
return;
}
delegate_->ShortWaitForElementWithSlowWarning(
selector_,
base::BindOnce(&UseAddressAction::OnWaitForElementTimed,
weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&UseAddressAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr())));
}
void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(element_status);
return;
}
if (proto_.use_address().skip_autofill()) {
ExecuteFallback(OkClientStatus());
return;
}
DCHECK(!selector_.empty());
DCHECK(profile_ != nullptr);
delegate_->FillAddressForm(profile_.get(), selector_,
base::BindOnce(&UseAddressAction::ExecuteFallback,
weak_ptr_factory_.GetWeakPtr()));
}
void UseAddressAction::ExecuteFallback(const ClientStatus& status) {
DCHECK(profile_ != nullptr);
std::vector<RequiredField> required_fields;
for (const auto& required_field_proto :
proto_.use_address().required_fields()) {
if (!required_field_proto.has_value_expression()) {
continue;
}
RequiredField required_field;
required_field.FromProto(required_field_proto);
required_fields.emplace_back(required_field);
}
DCHECK(fallback_handler_ == nullptr);
fallback_handler_ = std::make_unique<RequiredFieldsFallbackHandler>(
required_fields,
field_formatter::CreateAutofillMappings(*profile_,
/* locale = */ "en-US"),
delegate_);
fallback_handler_->CheckAndFallbackRequiredFields(
status, base::BindOnce(&UseAddressAction::EndAction,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace autofill_assistant