Commit 698ab388 authored by sandromaggi's avatar sandromaggi Committed by Commit Bot

[Autofill Assistant] Add profile value for field filling

This allows the SetFormFieldValueAction to use a value from a
previously selected profile. If the profile is not available the action
will terminate with an error. If the value expression cannot be
resolved the action will terminate with an error.

Bug: b/169521597
Change-Id: Id2502d366d42a085103261bb35e30b0e701410fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2435084
Commit-Queue: Sandro Maggi <sandromaggi@google.com>
Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#811652}
parent e0f82769
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h" #include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/actions/action_delegate_util.h" #include "components/autofill_assistant/browser/actions/action_delegate_util.h"
#include "components/autofill_assistant/browser/client_status.h" #include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/field_formatter.h"
namespace autofill_assistant { namespace autofill_assistant {
namespace { namespace {
...@@ -57,6 +59,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -57,6 +59,7 @@ void SetFormFieldValueAction::InternalProcessAction(
} }
// Check proto fields. // Check proto fields.
int keypress_index = 0;
for (const auto& keypress : proto_.set_form_value().value()) { for (const auto& keypress : proto_.set_form_value().value()) {
switch (keypress.keypress_case()) { switch (keypress.keypress_case()) {
case SetFormFieldValueProto_KeyPress::kKeycode: case SetFormFieldValueProto_KeyPress::kKeycode:
...@@ -68,7 +71,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -68,7 +71,7 @@ void SetFormFieldValueAction::InternalProcessAction(
VLOG(1) << "SetFormFieldValueAction: field `keycode' is deprecated " VLOG(1) << "SetFormFieldValueAction: field `keycode' is deprecated "
<< "and only supports US-ASCII values (encountered value > " << "and only supports US-ASCII values (encountered value > "
"127). Use field `key' instead."; "127). Use field `key' instead.";
EndAction(ClientStatus(INVALID_ACTION)); FailAction(ClientStatus(INVALID_ACTION), keypress_index);
return; return;
} }
field_inputs_.emplace_back( field_inputs_.emplace_back(
...@@ -79,7 +82,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -79,7 +82,7 @@ void SetFormFieldValueAction::InternalProcessAction(
if (keypress.keyboard_input().empty()) { if (keypress.keyboard_input().empty()) {
VLOG(1) << "SetFormFieldValueAction: field 'keyboard_input' must be " VLOG(1) << "SetFormFieldValueAction: field 'keyboard_input' must be "
"non-empty if set."; "non-empty if set.";
EndAction(ClientStatus(INVALID_ACTION)); FailAction(ClientStatus(INVALID_ACTION), keypress_index);
return; return;
} }
field_inputs_.emplace_back( field_inputs_.emplace_back(
...@@ -93,7 +96,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -93,7 +96,7 @@ void SetFormFieldValueAction::InternalProcessAction(
if (!delegate_->GetUserData()->selected_login_.has_value()) { if (!delegate_->GetUserData()->selected_login_.has_value()) {
VLOG(1) << "SetFormFieldValueAction: requested login details not " VLOG(1) << "SetFormFieldValueAction: requested login details not "
"available in client memory."; "available in client memory.";
EndAction(ClientStatus(PRECONDITION_FAILED)); FailAction(ClientStatus(PRECONDITION_FAILED), keypress_index);
return; return;
} }
if (keypress.keypress_case() == if (keypress.keypress_case() ==
...@@ -112,7 +115,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -112,7 +115,7 @@ void SetFormFieldValueAction::InternalProcessAction(
case SetFormFieldValueProto_KeyPress::kClientMemoryKey: case SetFormFieldValueProto_KeyPress::kClientMemoryKey:
if (keypress.client_memory_key().empty()) { if (keypress.client_memory_key().empty()) {
VLOG(1) << "SetFormFieldValueAction: empty |client_memory_key|"; VLOG(1) << "SetFormFieldValueAction: empty |client_memory_key|";
EndAction(ClientStatus(INVALID_ACTION)); FailAction(ClientStatus(INVALID_ACTION), keypress_index);
return; return;
} }
if (!delegate_->GetUserData()->has_additional_value( if (!delegate_->GetUserData()->has_additional_value(
...@@ -125,7 +128,7 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -125,7 +128,7 @@ void SetFormFieldValueAction::InternalProcessAction(
VLOG(1) << "SetFormFieldValueAction: requested key '" VLOG(1) << "SetFormFieldValueAction: requested key '"
<< keypress.client_memory_key() << keypress.client_memory_key()
<< "' not available in client memory"; << "' not available in client memory";
EndAction(ClientStatus(PRECONDITION_FAILED)); FailAction(ClientStatus(PRECONDITION_FAILED), keypress_index);
return; return;
} }
field_inputs_.emplace_back( field_inputs_.emplace_back(
...@@ -134,11 +137,43 @@ void SetFormFieldValueAction::InternalProcessAction( ...@@ -134,11 +137,43 @@ void SetFormFieldValueAction::InternalProcessAction(
->strings() ->strings()
.values(0)); .values(0));
break; break;
case SetFormFieldValueProto_KeyPress::kAutofillValue: {
if (keypress.autofill_value().profile().identifier().empty() ||
keypress.autofill_value().value_expression().empty()) {
VLOG(1) << "SetFormFieldValueAction: |autofill_value| with empty "
"|profile.identifier| or |value_expression|";
FailAction(ClientStatus(INVALID_ACTION), keypress_index);
return;
}
const autofill::AutofillProfile* address =
delegate_->GetUserData()->selected_address(
keypress.autofill_value().profile().identifier());
if (address == nullptr) {
VLOG(1) << "SetFormFieldValueAction: requested unknown address '"
<< keypress.autofill_value().profile().identifier() << "'";
FailAction(ClientStatus(PRECONDITION_FAILED), keypress_index);
return;
}
auto value = field_formatter::FormatString(
keypress.autofill_value().value_expression(),
field_formatter::CreateAutofillMappings(*address,
/* locale= */ "en-US"));
if (!value.has_value()) {
FailAction(ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE), keypress_index);
return;
}
field_inputs_.emplace_back(*value);
break;
}
default: default:
VLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress"; VLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
EndAction(ClientStatus(INVALID_ACTION)); FailAction(ClientStatus(INVALID_ACTION), keypress_index);
return; return;
} }
++keypress_index;
} }
delegate_->ShortWaitForElement( delegate_->ShortWaitForElement(
...@@ -278,6 +313,14 @@ void SetFormFieldValueAction::OnGetStoredPassword(int field_index, ...@@ -278,6 +313,14 @@ void SetFormFieldValueAction::OnGetStoredPassword(int field_index,
} }
} }
void SetFormFieldValueAction::FailAction(const ClientStatus& status,
int keypress_index) {
processed_action_proto_->mutable_status_details()
->mutable_form_field_error_info()
->set_invalid_keypress_index(keypress_index);
EndAction(status);
}
void SetFormFieldValueAction::EndAction(const ClientStatus& status) { void SetFormFieldValueAction::EndAction(const ClientStatus& status) {
// Clear immediately, to prevent sensitive information from staying in memory. // Clear immediately, to prevent sensitive information from staying in memory.
field_inputs_.clear(); field_inputs_.clear();
......
...@@ -70,6 +70,7 @@ class SetFormFieldValueAction : public Action { ...@@ -70,6 +70,7 @@ class SetFormFieldValueAction : public Action {
void OnGetStoredPassword(int field_index, bool success, std::string password); void OnGetStoredPassword(int field_index, bool success, std::string password);
void FailAction(const ClientStatus& status, int keypress_index);
void EndAction(const ClientStatus& status); void EndAction(const ClientStatus& status);
Selector selector_; Selector selector_;
......
...@@ -7,8 +7,14 @@ ...@@ -7,8 +7,14 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "base/guid.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gmock_callback_support.h" #include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill_assistant/browser/actions/action_test_utils.h" #include "components/autofill_assistant/browser/actions/action_test_utils.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h" #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h" #include "components/autofill_assistant/browser/client_status.h"
...@@ -357,4 +363,80 @@ TEST_F(SetFormFieldValueActionTest, PasswordIsClearedFromMemory) { ...@@ -357,4 +363,80 @@ TEST_F(SetFormFieldValueActionTest, PasswordIsClearedFromMemory) {
EXPECT_TRUE(action.field_inputs_.empty()); EXPECT_TRUE(action.field_inputs_.empty());
} }
TEST_F(SetFormFieldValueActionTest, EmptyProfileValueFails) {
set_form_field_proto_->add_value()->mutable_autofill_value();
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, RequestDataFromUnknownProfile) {
auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
value->mutable_profile()->set_identifier("none");
value->set_value_expression("value");
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
PRECONDITION_FAILED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, RequestUnknownDataFromProfile) {
autofill::AutofillProfile contact(base::GenerateGUID(),
autofill::test::kEmptyOrigin);
// Middle name is expected to be empty.
autofill::test::SetProfileInfo(&contact, "John", /* middle name */ "", "Doe",
"", "", "", "", "", "", "", "", "");
user_data_.selected_addresses_["contact"] =
std::make_unique<autofill::AutofillProfile>(contact);
auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
value->mutable_profile()->set_identifier("contact");
value->set_value_expression(
base::StrCat({"${",
base::NumberToString(static_cast<int>(
autofill::ServerFieldType::NAME_MIDDLE)),
"}"}));
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
AUTOFILL_INFO_NOT_AVAILABLE))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, SetFieldFromProfileValue) {
autofill::AutofillProfile contact(base::GenerateGUID(),
autofill::test::kEmptyOrigin);
autofill::test::SetProfileInfo(&contact, "John", "", "Doe", "", "", "", "",
"", "", "", "", "");
user_data_.selected_addresses_["contact"] =
std::make_unique<autofill::AutofillProfile>(contact);
auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
value->mutable_profile()->set_identifier("contact");
value->set_value_expression(
base::StrCat({"${",
base::NumberToString(static_cast<int>(
autofill::ServerFieldType::NAME_FIRST)),
"}"}));
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
.WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue("John", _, _,
EqualsElement(test_util::MockFindElement(
mock_action_delegate_, fake_selector_)),
_))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -459,6 +459,21 @@ enum ClickType { ...@@ -459,6 +459,21 @@ enum ClickType {
CLICK = 3; CLICK = 3;
} }
message AutofillValue {
message Profile { optional string identifier = 1; }
// The profile to be used. This has to be requested with a
// |CollectUserDataAction| first.
optional Profile profile = 1;
// A string containing any number of "${key}" placeholders, where the key is
// an integer corresponding to entries from field_types.h or
// |AutofillFormatProto::AutofillAssistantCustomField|.
// Note that the set of actually available fields are outside of our
// control and are retrieved automatically from the provided profile.
optional string value_expression = 2;
}
// An action could be performed. // An action could be performed.
message ActionProto { message ActionProto {
// Wait these many milliseconds before executing the action, if set. // Wait these many milliseconds before executing the action, if set.
...@@ -607,6 +622,9 @@ message ProcessedActionStatusDetailsProto { ...@@ -607,6 +622,9 @@ message ProcessedActionStatusDetailsProto {
// More information included for autofill related errors. // More information included for autofill related errors.
optional AutofillErrorInfoProto autofill_error_info = 3; optional AutofillErrorInfoProto autofill_error_info = 3;
// More information included for |SetFormFieldValueProto| related errors.
optional SetFormFieldErrorInfoProto form_field_error_info = 4;
} }
message NavigationInfoProto { message NavigationInfoProto {
...@@ -711,6 +729,13 @@ message AutofillErrorInfoProto { ...@@ -711,6 +729,13 @@ message AutofillErrorInfoProto {
repeated AutofillFieldError autofill_field_error = 5; repeated AutofillFieldError autofill_field_error = 5;
} }
// Message to report |SetFormFieldValueProto| related errors for debugging
// purposes.
message SetFormFieldErrorInfoProto {
// The index of |keypress| that caused the action to be invalid.
optional int32 invalid_keypress_index = 1;
}
// The pseudo type values come from // The pseudo type values come from
// https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-PseudoType. // https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-PseudoType.
enum PseudoType { enum PseudoType {
...@@ -993,9 +1018,9 @@ message UseAddressProto { ...@@ -993,9 +1018,9 @@ message UseAddressProto {
// Message used to indicate what form fields should be filled with what // Message used to indicate what form fields should be filled with what
// information coming from the address. // information coming from the address.
message RequiredField { message RequiredField {
// A string containing either a single integer key or multiple "${key}" // A string containing any number of "${key}" placeholders, where the key
// placeholders, where the key is an integer corresponding to entries from // is an integer corresponding to entries from field_types.h or
// field_types.h or AutofillFormatProto::AutofillAssistantCustomField. // |AutofillFormatProto::AutofillAssistantCustomField|.
// Example: // Example:
// * "3" -> First name. // * "3" -> First name.
// * "${3}" -> First name. // * "${3}" -> First name.
...@@ -1059,9 +1084,9 @@ message UseCreditCardProto { ...@@ -1059,9 +1084,9 @@ message UseCreditCardProto {
// Message used to indicate what form fields should be filled with what // Message used to indicate what form fields should be filled with what
// information. // information.
message RequiredField { message RequiredField {
// A string containing either a single integer key or multiple "${key}" // A string containing any number of "${key}" placeholders, where the key
// placeholders, where the key is an integer corresponding to entries from // is an integer corresponding to entries from field_types.h or
// field_types.h or AutofillFormatProto::AutofillAssistantCustomField. // |AutofillFormatProto::AutofillAssistantCustomField|.
// Example: // Example:
// * "51" -> Full name. // * "51" -> Full name.
// * "${51}" -> Full Name. // * "${51}" -> Full Name.
...@@ -1918,6 +1943,9 @@ message SetFormFieldValueProto { ...@@ -1918,6 +1943,9 @@ message SetFormFieldValueProto {
bool use_password = 5; bool use_password = 5;
// Use the value stored at the specified memory location. // Use the value stored at the specified memory location.
string client_memory_key = 6; string client_memory_key = 6;
// A value from an Autofill source. Note that this must be proceeded by a
// |CollectUserDataAction|.
AutofillValue autofill_value = 8;
} }
reserved 7; reserved 7;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment