blob: 10beaf322b4f8a8b610af98c513efaa283d5e1c0 [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/set_form_field_value_action.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant {
SetFormFieldValueAction::FieldInput::FieldInput(
std::unique_ptr<std::vector<UChar32>> _keyboard_input)
: keyboard_input(std::move(_keyboard_input)) {}
SetFormFieldValueAction::FieldInput::FieldInput(std::string _value)
: value(_value) {}
SetFormFieldValueAction::FieldInput::FieldInput(bool _use_password)
: use_password(_use_password) {}
SetFormFieldValueAction::FieldInput::FieldInput(FieldInput&& other) = default;
SetFormFieldValueAction::FieldInput::~FieldInput() {}
SetFormFieldValueAction::SetFormFieldValueAction(ActionDelegate* delegate,
const ActionProto& proto)
: Action(delegate, proto) {
DCHECK(proto_.has_set_form_value());
DCHECK_GT(proto_.set_form_value().element().selectors_size(), 0);
DCHECK_GT(proto_.set_form_value().value_size(), 0);
}
SetFormFieldValueAction::~SetFormFieldValueAction() {}
void SetFormFieldValueAction::InternalProcessAction(
ProcessActionCallback callback) {
process_action_callback_ = std::move(callback);
selector_ = Selector(proto_.set_form_value().element()).MustBeVisible();
if (selector_.empty()) {
DVLOG(1) << __func__ << ": empty selector";
EndAction(ClientStatus(INVALID_SELECTOR));
return;
}
// Check proto fields.
for (const auto& keypress : proto_.set_form_value().value()) {
switch (keypress.keypress_case()) {
case SetFormFieldValueProto_KeyPress::kKeycode:
// DEPRECATED: the field `keycode' used to contain a single character to
// input as text. Since there is no easy way to convert keycodes to
// text, this field is now deprecated and only works for US-ASCII
// characters. You should use the `keyboard_input' field instead.
if (keypress.keycode() >= 128) {
DVLOG(1) << "SetFormFieldValueAction: field `keycode' is deprecated "
<< "and only supports US-ASCII values (encountered "
<< keypress.keycode() << "). Use field `key' instead.";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
field_inputs_.emplace_back(
/* keyboard_input = */ std::make_unique<std::vector<UChar32>>(
1, keypress.keycode()));
break;
case SetFormFieldValueProto_KeyPress::kKeyboardInput:
if (keypress.keyboard_input().empty()) {
DVLOG(1) << "SetFormFieldValueAction: field 'keyboard_input' must be "
"non-empty if set.";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
field_inputs_.emplace_back(
/* keyboard_input = */ std::make_unique<std::vector<UChar32>>(
UTF8ToUnicode(keypress.keyboard_input())));
break;
case SetFormFieldValueProto_KeyPress::kUseUsername:
FALLTHROUGH;
case SetFormFieldValueProto_KeyPress::kUsePassword:
// Login information must have been stored by a previous action.
if (!delegate_->GetClientMemory()->has_selected_login()) {
DVLOG(1) << "SetFormFieldValueAction: requested login details not "
"available in client memory.";
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
if (keypress.keypress_case() ==
SetFormFieldValueProto_KeyPress::kUseUsername) {
field_inputs_.emplace_back(/* value = */ delegate_->GetClientMemory()
->selected_login()
->username);
} else {
field_inputs_.emplace_back(/* use_password = */ true);
}
break;
case SetFormFieldValueProto_KeyPress::kText:
// Currently no check required.
field_inputs_.emplace_back(/* value = */ keypress.text());
break;
default:
DVLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
}
delegate_->ShortWaitForElement(
selector_, base::BindOnce(&SetFormFieldValueAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr()));
}
void SetFormFieldValueAction::OnWaitForElement(
const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(ClientStatus(element_status.proto_status()));
return;
}
// Start with first value, then call OnSetFieldValue() recursively until done.
OnSetFieldValue(/* next = */ 0, OkClientStatus());
}
void SetFormFieldValueAction::OnSetFieldValue(int next,
const ClientStatus& status) {
// If something went wrong or we are out of values: finish
if (!status.ok() || next >= proto_.set_form_value().value_size()) {
EndAction(status);
return;
}
bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
auto next_field_callback = base::BindOnce(
&SetFormFieldValueAction::OnSetFieldValue, weak_ptr_factory_.GetWeakPtr(),
/* next = */ next + 1);
for (const auto& field_input : field_inputs_) {
if (field_input.keyboard_input) {
delegate_->SendKeyboardInput(selector_, *field_input.keyboard_input,
delay_in_millisecond,
std::move(next_field_callback));
} else if (field_input.use_password) {
delegate_->GetWebsiteLoginFetcher()->GetPasswordForLogin(
*delegate_->GetClientMemory()->selected_login(),
base::BindOnce(&SetFormFieldValueAction::OnGetPassword,
weak_ptr_factory_.GetWeakPtr(),
/* field_index = */ next));
} else {
if (simulate_key_presses || field_input.value.empty()) {
delegate_->SetFieldValue(selector_, field_input.value,
simulate_key_presses, delay_in_millisecond,
std::move(next_field_callback));
} else {
delegate_->SetFieldValue(
selector_, field_input.value, simulate_key_presses,
delay_in_millisecond,
base::BindOnce(
&SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
weak_ptr_factory_.GetWeakPtr(),
/* field_index = */ next,
/* requested_value = */ field_input.value));
}
}
}
}
void SetFormFieldValueAction::OnSetFieldValueAndCheckFallback(
int field_index,
const std::string& requested_value,
const ClientStatus& status) {
if (!status.ok()) {
EndAction(status);
return;
}
delegate_->GetFieldValue(
selector_, base::BindOnce(&SetFormFieldValueAction::OnGetFieldValue,
weak_ptr_factory_.GetWeakPtr(), field_index,
requested_value));
}
void SetFormFieldValueAction::OnGetFieldValue(
int field_index,
const std::string& requested_value,
const ClientStatus& element_status,
const std::string& actual_value) {
// Move to next value if |GetFieldValue| failed.
if (!element_status.ok()) {
OnSetFieldValue(field_index + 1, OkClientStatus());
return;
}
// If value is still empty while it is not supposed to be, trigger keyboard
// simulation fallback.
if (!requested_value.empty() && actual_value.empty()) {
// Report a key press simulation fallback has happened.
auto result = SetFormFieldValueProto::Result();
result.set_fallback_to_simulate_key_presses(true);
*processed_action_proto_->mutable_set_form_field_value_result() = result;
// Run |SetFieldValue| with keyboard simulation on and move on to next value
// afterwards.
delegate_->SetFieldValue(
selector_, requested_value, /*simulate_key_presses = */ true,
proto_.set_form_value().delay_in_millisecond(),
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(),
/* next = */ field_index + 1));
return;
}
// Move to next value in all other cases.
OnSetFieldValue(field_index + 1, OkClientStatus());
}
void SetFormFieldValueAction::OnGetPassword(int field_index,
bool success,
std::string password) {
if (!success) {
EndAction(ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE));
return;
}
bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
if (simulate_key_presses) {
delegate_->SetFieldValue(
selector_, password, simulate_key_presses, delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(),
/* next = */ field_index + 1));
} else {
delegate_->SetFieldValue(
selector_, password, simulate_key_presses, delay_in_millisecond,
base::BindOnce(
&SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
weak_ptr_factory_.GetWeakPtr(),
/* next = */ field_index, /* requested_value = */ password));
}
}
void SetFormFieldValueAction::EndAction(const ClientStatus& status) {
// Clear immediately, to prevent sensitive information from staying in memory.
field_inputs_.clear();
UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant