Commit 8667827a authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] Adds action to use login credentials.

This CL extends SetFormFieldValueAction such that it can also fill a selector with a previously stored username or password. To this end, the action is refactored to allow for better code reuse and readability. Also, the CL adds unit tests (not just for the new functionality, but for the entire action).


Bug: b/128833341
Change-Id: I43e57bee5681047f1df954040f62265e0ab44f28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1728560
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarStephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#689877}
parent 9de9a904
......@@ -179,6 +179,7 @@ source_set("unit_tests") {
"actions/mock_action_delegate.h",
"actions/popup_message_action_unittest.cc",
"actions/prompt_action_unittest.cc",
"actions/set_form_field_value_action_unittest.cc",
"actions/wait_for_document_action_unittest.cc",
"actions/wait_for_dom_action_unittest.cc",
"batch_element_checker_unittest.cc",
......@@ -193,6 +194,8 @@ source_set("unit_tests") {
"mock_service.h",
"mock_web_controller.cc",
"mock_web_controller.h",
"mock_website_login_fetcher.cc",
"mock_website_login_fetcher.h",
"protocol_utils_unittest.cc",
"retry_timer_unittest.cc",
"script_executor_unittest.cc",
......
......@@ -146,7 +146,7 @@ class MockActionDelegate : public ActionDelegate {
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
OnSetFieldValue(selector, value, callback);
OnSetFieldValue(selector, value, delay_in_millisecond, delay_in_millisecond,
OnSetFieldValue(selector, value, simulate_key_presses, delay_in_millisecond,
callback);
}
......@@ -168,11 +168,20 @@ class MockActionDelegate : public ActionDelegate {
const std::string& value,
base::OnceCallback<void(const ClientStatus&)> callback));
MOCK_METHOD4(SendKeyboardInput,
void SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
OnSendKeyboardInput(selector, codepoints, delay_in_millisecond, callback);
}
MOCK_METHOD4(OnSendKeyboardInput,
void(const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback));
base::OnceCallback<void(const ClientStatus&)>& callback));
MOCK_METHOD2(GetOuterHtml,
void(const Selector& selector,
base::OnceCallback<void(const ClientStatus&,
......
......@@ -10,157 +10,188 @@
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/string_conversions_util.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);
DCHECK(proto_.set_form_value().value(0).has_text());
}
SetFormFieldValueAction::~SetFormFieldValueAction() {}
void SetFormFieldValueAction::InternalProcessAction(
ProcessActionCallback callback) {
Selector selector =
Selector(proto_.set_form_value().element()).MustBeVisible();
if (selector.empty()) {
process_action_callback_ = std::move(callback);
selector_ = Selector(proto_.set_form_value().element()).MustBeVisible();
if (selector_.empty()) {
DVLOG(1) << __func__ << ": empty selector";
UpdateProcessedAction(INVALID_SELECTOR);
std::move(callback).Run(std::move(processed_action_proto_));
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(),
std::move(callback), selector));
selector_, base::BindOnce(&SetFormFieldValueAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr()));
}
void SetFormFieldValueAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
bool element_found) {
void SetFormFieldValueAction::OnWaitForElement(bool element_found) {
if (!element_found) {
UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
std::move(callback).Run(std::move(processed_action_proto_));
EndAction(ClientStatus(ELEMENT_RESOLUTION_FAILED));
return;
}
// Start with first value, then call OnSetFieldValue() recursively until done.
OnSetFieldValue(std::move(callback), selector, /* next = */ 0,
OkClientStatus());
OnSetFieldValue(/* next = */ 0, OkClientStatus());
}
void SetFormFieldValueAction::OnSetFieldValue(ProcessActionCallback callback,
const Selector& selector,
int next,
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()) {
UpdateProcessedAction(status);
std::move(callback).Run(std::move(processed_action_proto_));
EndAction(status);
return;
}
const auto& key_field = proto_.set_form_value().value(next);
bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
switch (key_field.keypress_case()) {
case SetFormFieldValueProto_KeyPress::kText:
if (simulate_key_presses || key_field.text().empty()) {
// If we are already using keyboard simulation or we are trying to set
// an empty value, no need to trigger keyboard fallback. Simply move on
// to next value after |SetFieldValue| is done.
delegate_->SetFieldValue(
selector, key_field.text(), simulate_key_presses,
delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
selector,
/* next = */ next + 1));
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 {
// Trigger a check for keyboard fallback when |SetFieldValue| is done.
delegate_->SetFieldValue(
selector, key_field.text(), simulate_key_presses,
selector_, field_input.value, simulate_key_presses,
delay_in_millisecond,
base::BindOnce(
&SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), selector,
/* next = */ next));
weak_ptr_factory_.GetWeakPtr(),
/* field_index = */ next,
/* requested_value = */ field_input.value));
}
break;
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 (key_field.keycode() < 128) { // US-ASCII
delegate_->SendKeyboardInput(
selector, {key_field.keycode()}, delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
selector,
/* next = */ next + 1));
} else {
DVLOG(3)
<< "SetFormFieldValueProto_KeyPress: field `keycode' is deprecated "
<< "and only supports US-ASCII values (encountered "
<< key_field.keycode() << "). Use field `key' instead.";
OnSetFieldValue(std::move(callback), selector, next,
ClientStatus(INVALID_ACTION));
}
break;
case SetFormFieldValueProto_KeyPress::kKeyboardInput:
delegate_->SendKeyboardInput(
selector, UTF8ToUnicode(key_field.keyboard_input()),
delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
selector,
/* next = */ next + 1));
break;
default:
DVLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
OnSetFieldValue(std::move(callback), selector, next,
ClientStatus(INVALID_ACTION));
break;
}
}
}
void SetFormFieldValueAction::OnSetFieldValueAndCheckFallback(
ProcessActionCallback callback,
const Selector& selector,
int next,
int field_index,
const std::string& requested_value,
const ClientStatus& status) {
if (!status.ok()) {
OnSetFieldValue(std::move(callback), selector, next + 1, status);
EndAction(status);
return;
}
delegate_->GetFieldValue(
selector, base::BindOnce(&SetFormFieldValueAction::OnGetFieldValue,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback), selector, next));
selector_, base::BindOnce(&SetFormFieldValueAction::OnGetFieldValue,
weak_ptr_factory_.GetWeakPtr(), field_index,
requested_value));
}
void SetFormFieldValueAction::OnGetFieldValue(ProcessActionCallback callback,
const Selector& selector,
int next,
bool get_value_status,
const std::string& value) {
const auto& key_field = proto_.set_form_value().value(next);
void SetFormFieldValueAction::OnGetFieldValue(
int field_index,
const std::string& requested_value,
bool get_value_status,
const std::string& actual_value) {
// Move to next value if |GetFieldValue| failed.
if (!get_value_status) {
OnSetFieldValue(std::move(callback), selector, next + 1, OkClientStatus());
OnSetFieldValue(field_index + 1, OkClientStatus());
return;
}
// If value is still empty while it is not supposed to be, trigger keyboard
// simulation fallback.
if (key_field.text().size() > 0 && value.empty()) {
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);
......@@ -169,17 +200,46 @@ void SetFormFieldValueAction::OnGetFieldValue(ProcessActionCallback callback,
// Run |SetFieldValue| with keyboard simulation on and move on to next value
// afterwards.
delegate_->SetFieldValue(
selector, key_field.text(), /*simulate_key_presses = */ true,
selector_, requested_value, /*simulate_key_presses = */ true,
proto_.set_form_value().delay_in_millisecond(),
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
selector,
/* next = */ next + 1));
weak_ptr_factory_.GetWeakPtr(),
/* next = */ field_index + 1));
return;
}
// Move to next value in all other cases.
OnSetFieldValue(std::move(callback), selector, next + 1, OkClientStatus());
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) {
UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant
......@@ -8,9 +8,11 @@
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/string_conversions_util.h"
namespace autofill_assistant {
......@@ -22,29 +24,48 @@ class SetFormFieldValueAction : public Action {
~SetFormFieldValueAction() override;
private:
// A field input as extracted from the proto, but already checked for
// validity.
struct FieldInput {
explicit FieldInput(std::unique_ptr<std::vector<UChar32>> keyboard_input);
explicit FieldInput(std::string value);
explicit FieldInput(bool use_password);
FieldInput(FieldInput&& other);
~FieldInput();
// The keys to press if either |keycode| or |keyboard_input| is set, else
// nullptr.
std::unique_ptr<std::vector<UChar32>> keyboard_input = nullptr;
// True if the value should be retrieved from the login details in client
// memory.
bool use_password = false;
// The string to input (for all other cases).
std::string value;
};
// Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override;
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
bool element_found);
void OnWaitForElement(bool element_found);
void OnGetFieldValue(ProcessActionCallback callback,
const Selector& selector,
int next,
void OnGetFieldValue(int field_index,
const std::string& requested_value,
bool status,
const std::string& value);
const std::string& actual_value);
void OnSetFieldValue(ProcessActionCallback callback,
const Selector& selector,
int next,
const ClientStatus& status);
void OnSetFieldValue(int next, const ClientStatus& status);
void OnSetFieldValueAndCheckFallback(ProcessActionCallback callback,
const Selector& selector,
int next,
void OnSetFieldValueAndCheckFallback(int field_index,
const std::string& requested_value,
const ClientStatus& status);
void OnGetPassword(int field_index, bool success, std::string password);
void EndAction(const ClientStatus& status);
Selector selector_;
std::vector<FieldInput> field_inputs_;
ProcessActionCallback process_action_callback_;
base::WeakPtrFactory<SetFormFieldValueAction> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SetFormFieldValueAction);
......
// Copyright 2019 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 <string>
#include <utility>
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/client_memory.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/mock_website_login_fetcher.h"
#include "components/autofill_assistant/browser/string_conversions_util.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace {
const char kFakeUrl[] = "https://www.example.com";
const char kFakeSelector[] = "#some_selector";
const char kFakeUsername[] = "user@example.com";
const char kFakePassword[] = "example_password";
} // namespace
namespace autofill_assistant {
namespace {
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::InSequence;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::Return;
class SetFormFieldValueActionTest : public testing::Test {
public:
void SetUp() override {
set_form_field_proto_ = proto_.mutable_set_form_value();
set_form_field_proto_->mutable_element()->add_selectors(kFakeSelector);
set_form_field_proto_->mutable_element()->set_visibility_requirement(
MUST_BE_VISIBLE);
ON_CALL(mock_action_delegate_, GetClientMemory)
.WillByDefault(Return(&client_memory_));
ON_CALL(mock_action_delegate_, GetWebsiteLoginFetcher)
.WillByDefault(Return(&mock_website_login_fetcher_));
ON_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
.WillByDefault(RunOnceCallback<1>(true));
ON_CALL(mock_action_delegate_, OnSetFieldValue(_, _, _, _, _))
.WillByDefault(RunOnceCallback<4>(OkClientStatus()));
ON_CALL(mock_website_login_fetcher_, OnGetLoginsForUrl(_, _))
.WillByDefault(
RunOnceCallback<1>(std::vector<WebsiteLoginFetcher::Login>{
WebsiteLoginFetcher::Login(GURL(kFakeUrl), kFakeUsername)}));
ON_CALL(mock_website_login_fetcher_, OnGetPasswordForLogin(_, _))
.WillByDefault(RunOnceCallback<1>(true, kFakePassword));
client_memory_.set_selected_login({GURL(kFakeUrl), kFakeUsername});
fake_selector_ = Selector({kFakeSelector}).MustBeVisible();
}
protected:
Selector fake_selector_;
MockActionDelegate mock_action_delegate_;
MockWebsiteLoginFetcher mock_website_login_fetcher_;
base::MockCallback<Action::ProcessActionCallback> callback_;
ActionProto proto_;
SetFormFieldValueProto* set_form_field_proto_;
ClientMemory client_memory_;
};
TEST_F(SetFormFieldValueActionTest, RequestedUsernameButNoLoginInClientMemory) {
ClientMemory empty_client_memory;
ON_CALL(mock_action_delegate_, GetClientMemory)
.WillByDefault(Return(&empty_client_memory));
auto* value = set_form_field_proto_->add_value();
value->set_use_username(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
PRECONDITION_FAILED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, RequestedPasswordButNoLoginInClientMemory) {
ClientMemory empty_client_memory;
ON_CALL(mock_action_delegate_, GetClientMemory)
.WillByDefault(Return(&empty_client_memory));
auto* value = set_form_field_proto_->add_value();
value->set_use_password(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
PRECONDITION_FAILED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, RequestedPasswordButPasswordNotAvailable) {
ON_CALL(mock_website_login_fetcher_, OnGetPasswordForLogin(_, _))
.WillByDefault(RunOnceCallback<1>(false, std::string()));
auto* value = set_form_field_proto_->add_value();
value->set_use_password(true);
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, NonAsciiKeycode) {
auto* value = set_form_field_proto_->add_value();
value->set_keycode(UTF8ToUnicode("𠜎")[0]);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, Username) {
auto* value = set_form_field_proto_->add_value();
value->set_use_username(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
.WillByDefault(RunOnceCallback<1>(true, kFakeUsername));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, kFakeUsername, _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, Password) {
auto* value = set_form_field_proto_->add_value();
value->set_use_password(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
.WillByDefault(RunOnceCallback<1>(true, kFakePassword));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, kFakePassword, _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, Keycode) {
auto* value = set_form_field_proto_->add_value();
value->set_keycode(13); // carriage return
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(mock_action_delegate_,
OnSendKeyboardInput(fake_selector_, std::vector<int>{13}, _, _))
.WillOnce(RunOnceCallback<3>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, KeyboardInput) {
auto* value = set_form_field_proto_->add_value();
std::string keyboard_input = "SomeQuery𠜎\r";
value->set_keyboard_input(keyboard_input);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
EXPECT_CALL(
mock_action_delegate_,
OnSendKeyboardInput(fake_selector_, UTF8ToUnicode(keyboard_input), _, _))
.WillOnce(RunOnceCallback<3>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
TEST_F(SetFormFieldValueActionTest, Text) {
auto* value = set_form_field_proto_->add_value();
value->set_text("SomeText𠜎");
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
.WillByDefault(RunOnceCallback<1>(true, "SomeText𠜎"));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, "SomeText𠜎", _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
action.ProcessAction(callback_.Get());
}
// Test that automatic fallback to simulate keystrokes works.
TEST_F(SetFormFieldValueActionTest, Fallback) {
auto* value = set_form_field_proto_->add_value();
value->set_text("123");
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
.WillByDefault(RunOnceCallback<1>(true, ""));
{
InSequence seq;
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, "123",
/* simulate_key_presses = */ false, _, _));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, "123",
/* simulate_key_presses = */ true, _, _));
}
EXPECT_CALL(callback_,
Run(Pointee(AllOf(
Property(&ProcessedActionProto::status, ACTION_APPLIED),
Property(&ProcessedActionProto::set_form_field_value_result,
Property(&SetFormFieldValueProto::Result::
fallback_to_simulate_key_presses,
true))))));
action.ProcessAction(callback_.Get());
}
} // namespace
} // namespace autofill_assistant
\ No newline at end of file
......@@ -122,6 +122,10 @@ std::ostream& operator<<(std::ostream& out,
out << "NAVIGATION_ERROR";
break;
case ProcessedActionStatusProto::AUTOFILL_INFO_NOT_AVAILABLE:
out << "AUTOFILL_INFO_NOT_AVAILABLE";
break;
// Intentionally no default case to make compilation fail if a new value
// was added to the enum but not to this list.
}
......
// Copyright 2019 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/mock_website_login_fetcher.h"
namespace autofill_assistant {
MockWebsiteLoginFetcher::MockWebsiteLoginFetcher() {}
MockWebsiteLoginFetcher::~MockWebsiteLoginFetcher() {}
} // namespace autofill_assistant
// Copyright 2019 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.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
#include "base/callback.h"
#include "base/macros.h"
#include "components/autofill_assistant/browser/website_login_fetcher.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
// Mock login fetcher for unit tests.
class MockWebsiteLoginFetcher : public WebsiteLoginFetcher {
public:
MockWebsiteLoginFetcher();
~MockWebsiteLoginFetcher() override;
void GetLoginsForUrl(
const GURL& url,
base::OnceCallback<void(std::vector<Login>)> callback) override {
OnGetLoginsForUrl(url, callback);
}
MOCK_METHOD2(OnGetLoginsForUrl,
void(const GURL& domain,
base::OnceCallback<void(std::vector<Login>)>&));
void GetPasswordForLogin(
const Login& login,
base::OnceCallback<void(bool, std::string)> callback) override {
OnGetPasswordForLogin(login, callback);
}
MOCK_METHOD2(OnGetPasswordForLogin,
void(const Login& login,
base::OnceCallback<void(bool, std::string)>&));
DISALLOW_COPY_AND_ASSIGN(MockWebsiteLoginFetcher);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_WEBSITE_LOGIN_FETCHER_H_
......@@ -650,6 +650,10 @@ enum ProcessedActionStatusProto {
// A selector included into the current action is invalid.
INVALID_SELECTOR = 20;
// The requested autofill info (e.g., Chrome password manager login) was not
// available. It might have been recently deleted.
AUTOFILL_INFO_NOT_AVAILABLE = 21;
}
// The pseudo type values come from
......@@ -855,7 +859,8 @@ message UseAddressProto {
repeated RequiredField required_fields = 6;
}
// Fill a form with a credit card if there is, otherwise fail this action.
// Fill a form with a credit card if there is one stored in client memory,
// otherwise fail this action.
message UseCreditCardProto {
// Message used to indicate what form fields should be filled with what
// information.
......@@ -1323,6 +1328,12 @@ message SetFormFieldValueProto {
// layout. This can also be used for keyboard control sequences such
// as "\r" or "\t".
string keyboard_input = 3;
// Use the username from the Chrome password manager login previously
// selected in a GetPaymentInformationAction.
bool use_username = 4;
// Use the password from the Chrome password manager login previously
// selected in a GetPaymentInformationAction.
bool use_password = 5;
}
}
......
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