summaryrefslogtreecommitdiff
path: root/chromium/components/autofill_assistant
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 17:21:03 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 16:25:15 +0000
commitc551f43206405019121bd2b2c93714319a0a3300 (patch)
tree1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/components/autofill_assistant
parent7961cea6d1041e3e454dae6a1da660b453efd238 (diff)
downloadqtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/autofill_assistant')
-rw-r--r--chromium/components/autofill_assistant/browser/BUILD.gn11
-rw-r--r--chromium/components/autofill_assistant/browser/actions/action_delegate.h16
-rw-r--r--chromium/components/autofill_assistant/browser/actions/autofill_action.cc372
-rw-r--r--chromium/components/autofill_assistant/browser/actions/autofill_action.h158
-rw-r--r--chromium/components/autofill_assistant/browser/actions/click_action.cc7
-rw-r--r--chromium/components/autofill_assistant/browser/actions/click_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc228
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h1
-rw-r--r--chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc497
-rw-r--r--chromium/components/autofill_assistant/browser/actions/focus_element_action.cc7
-rw-r--r--chromium/components/autofill_assistant/browser/actions/focus_element_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/highlight_element_action.cc12
-rw-r--r--chromium/components/autofill_assistant/browser/actions/highlight_element_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h44
-rw-r--r--chromium/components/autofill_assistant/browser/actions/prompt_action.cc6
-rw-r--r--chromium/components/autofill_assistant/browser/actions/prompt_action.h3
-rw-r--r--chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc16
-rw-r--r--chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.cc186
-rw-r--r--chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.h142
-rw-r--r--chromium/components/autofill_assistant/browser/actions/select_option_action.cc7
-rw-r--r--chromium/components/autofill_assistant/browser/actions/select_option_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc7
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_attribute_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.cc31
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.h8
-rw-r--r--chromium/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc51
-rw-r--r--chromium/components/autofill_assistant/browser/actions/show_progress_bar_action.cc4
-rw-r--r--chromium/components/autofill_assistant/browser/actions/upload_dom_action.cc6
-rw-r--r--chromium/components/autofill_assistant/browser/actions/upload_dom_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_address_action.cc169
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_address_action.h76
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_address_action_unittest.cc345
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_credit_card_action.cc179
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_credit_card_action.h73
-rw-r--r--chromium/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc (renamed from chromium/components/autofill_assistant/browser/actions/autofill_action_unittest.cc)348
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc10
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h2
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_document_action_unittest.cc5
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.cc46
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.h18
-rw-r--r--chromium/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc40
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker.cc8
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker.h9
-rw-r--r--chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc44
-rw-r--r--chromium/components/autofill_assistant/browser/client_memory.cc23
-rw-r--r--chromium/components/autofill_assistant/browser/client_memory.h11
-rw-r--r--chromium/components/autofill_assistant/browser/client_settings.cc34
-rw-r--r--chromium/components/autofill_assistant/browser/client_settings.h15
-rw-r--r--chromium/components/autofill_assistant/browser/client_status.cc15
-rw-r--r--chromium/components/autofill_assistant/browser/controller.cc87
-rw-r--r--chromium/components/autofill_assistant/browser/controller.h18
-rw-r--r--chromium/components/autofill_assistant/browser/controller_observer.cc27
-rw-r--r--chromium/components/autofill_assistant/browser/controller_observer.h36
-rw-r--r--chromium/components/autofill_assistant/browser/controller_unittest.cc100
-rw-r--r--chromium/components/autofill_assistant/browser/details.cc262
-rw-r--r--chromium/components/autofill_assistant/browser/details.h56
-rw-r--r--chromium/components/autofill_assistant/browser/details_unittest.cc319
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template12
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template5
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/devtools_client.cc152
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/devtools_client.h50
-rw-r--r--chromium/components/autofill_assistant/browser/devtools/message_dispatcher.h3
-rw-r--r--chromium/components/autofill_assistant/browser/element_precondition.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/element_precondition.h7
-rw-r--r--chromium/components/autofill_assistant/browser/element_precondition_unittest.cc12
-rw-r--r--chromium/components/autofill_assistant/browser/mock_controller_observer.h7
-rw-r--r--chromium/components/autofill_assistant/browser/protocol_utils.cc10
-rw-r--r--chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc107
-rw-r--r--chromium/components/autofill_assistant/browser/retry_timer.cc12
-rw-r--r--chromium/components/autofill_assistant/browser/retry_timer.h13
-rw-r--r--chromium/components/autofill_assistant/browser/retry_timer_unittest.cc52
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor.cc52
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor.h60
-rw-r--r--chromium/components/autofill_assistant/browser/script_executor_unittest.cc30
-rw-r--r--chromium/components/autofill_assistant/browser/script_precondition_unittest.cc6
-rw-r--r--chromium/components/autofill_assistant/browser/script_tracker_unittest.cc11
-rw-r--r--chromium/components/autofill_assistant/browser/service.proto151
-rw-r--r--chromium/components/autofill_assistant/browser/state.h7
-rw-r--r--chromium/components/autofill_assistant/browser/trigger_context.cc12
-rw-r--r--chromium/components/autofill_assistant/browser/trigger_context.h9
-rw-r--r--chromium/components/autofill_assistant/browser/trigger_context_unittest.cc22
-rw-r--r--chromium/components/autofill_assistant/browser/ui_delegate.h27
-rw-r--r--chromium/components/autofill_assistant/browser/user_data.cc17
-rw-r--r--chromium/components/autofill_assistant/browser/user_data.h32
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder.cc102
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_finder.h10
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_position_getter.cc13
-rw-r--r--chromium/components/autofill_assistant/browser/web/element_position_getter.h5
-rw-r--r--chromium/components/autofill_assistant/browser/web/mock_web_controller.h20
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller.cc163
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller.h35
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc132
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_util.cc7
-rw-r--r--chromium/components/autofill_assistant/browser/web/web_controller_util.h3
-rw-r--r--chromium/components/autofill_assistant/browser/website_login_fetcher.h2
-rw-r--r--chromium/components/autofill_assistant/browser/website_login_fetcher_impl.cc6
96 files changed, 4061 insertions, 1533 deletions
diff --git a/chromium/components/autofill_assistant/browser/BUILD.gn b/chromium/components/autofill_assistant/browser/BUILD.gn
index 9ddc5a1b583..68bf9050ba1 100644
--- a/chromium/components/autofill_assistant/browser/BUILD.gn
+++ b/chromium/components/autofill_assistant/browser/BUILD.gn
@@ -34,8 +34,6 @@ jumbo_static_library("browser") {
"actions/action.cc",
"actions/action.h",
"actions/action_delegate.h",
- "actions/autofill_action.cc",
- "actions/autofill_action.h",
"actions/click_action.cc",
"actions/click_action.h",
"actions/collect_user_data_action.cc",
@@ -54,6 +52,8 @@ jumbo_static_library("browser") {
"actions/popup_message_action.h",
"actions/prompt_action.cc",
"actions/prompt_action.h",
+ "actions/required_fields_fallback_handler.cc",
+ "actions/required_fields_fallback_handler.h",
"actions/reset_action.cc",
"actions/reset_action.h",
"actions/select_option_action.cc",
@@ -78,6 +78,10 @@ jumbo_static_library("browser") {
"actions/unsupported_action.h",
"actions/upload_dom_action.cc",
"actions/upload_dom_action.h",
+ "actions/use_address_action.cc",
+ "actions/use_address_action.h",
+ "actions/use_credit_card_action.cc",
+ "actions/use_credit_card_action.h",
"actions/wait_for_document_action.cc",
"actions/wait_for_document_action.h",
"actions/wait_for_dom_action.cc",
@@ -185,7 +189,6 @@ jumbo_static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
- "actions/autofill_action_unittest.cc",
"actions/collect_user_data_action_unittest.cc",
"actions/configure_bottom_sheet_action_unittest.cc",
"actions/mock_action_delegate.cc",
@@ -194,6 +197,8 @@ source_set("unit_tests") {
"actions/prompt_action_unittest.cc",
"actions/set_form_field_value_action_unittest.cc",
"actions/show_details_action_unittest.cc",
+ "actions/use_address_action_unittest.cc",
+ "actions/use_credit_card_action_unittest.cc",
"actions/wait_for_document_action_unittest.cc",
"actions/wait_for_dom_action_unittest.cc",
"batch_element_checker_unittest.cc",
diff --git a/chromium/components/autofill_assistant/browser/actions/action_delegate.h b/chromium/components/autofill_assistant/browser/actions/action_delegate.h
index 74360d76adb..e418be400cc 100644
--- a/chromium/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/chromium/components/autofill_assistant/browser/actions/action_delegate.h
@@ -74,8 +74,9 @@ class ActionDelegate {
//
// TODO(crbug.com/806868): Consider embedding that wait right into
// WebController and eliminate double-lookup.
- virtual void ShortWaitForElement(const Selector& selector,
- base::OnceCallback<void(bool)> callback) = 0;
+ virtual void ShortWaitForElement(
+ const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&)> callback) = 0;
// Wait for up to |max_wait_time| for element conditions to match on the page,
// then call |callback| with a successful status if at least an element
@@ -85,10 +86,10 @@ class ActionDelegate {
virtual void WaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
- check_elements,
- base::OnceCallback<void(ProcessedActionStatusProto)> callback) = 0;
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements,
+ base::OnceCallback<void(const ClientStatus&)> callback) = 0;
// Click or tap the element given by |selector| on the web page.
virtual void ClickOrTapElement(
@@ -167,7 +168,8 @@ class ActionDelegate {
// empty string in case of error or empty value.
virtual void GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) = 0;
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback) = 0;
// Set the |value| of field |selector| and return the result through
// |callback|. If |simulate_key_presses| is true, the value will be set by
diff --git a/chromium/components/autofill_assistant/browser/actions/autofill_action.cc b/chromium/components/autofill_assistant/browser/actions/autofill_action.cc
deleted file mode 100644
index 4f67d3fda67..00000000000
--- a/chromium/components/autofill_assistant/browser/actions/autofill_action.cc
+++ /dev/null
@@ -1,372 +0,0 @@
-// 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/autofill_action.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill_assistant/browser/actions/action_delegate.h"
-#include "components/autofill_assistant/browser/batch_element_checker.h"
-#include "components/autofill_assistant/browser/client_memory.h"
-#include "components/autofill_assistant/browser/client_status.h"
-
-namespace autofill_assistant {
-
-AutofillAction::AutofillAction(ActionDelegate* delegate,
- const ActionProto& proto)
- : Action(delegate, proto) {
- if (proto.has_use_address()) {
- is_autofill_card_ = false;
- prompt_ = proto.use_address().prompt();
- name_ = proto.use_address().name();
- selector_ = Selector(proto.use_address().form_field_element());
- for (const auto& required_field_proto :
- proto_.use_address().required_fields()) {
- required_fields_.emplace_back();
- RequiredField& required_field = required_fields_.back();
- required_field.address_field = required_field_proto.address_field();
- required_field.selector = Selector(required_field_proto.element());
- required_field.simulate_key_presses =
- required_field_proto.simulate_key_presses();
- required_field.delay_in_millisecond =
- required_field_proto.delay_in_millisecond();
- required_field.forced = required_field_proto.forced();
- }
- } else {
- DCHECK(proto.has_use_card());
- is_autofill_card_ = true;
- prompt_ = proto.use_card().prompt();
- name_ = "";
- selector_ = Selector(proto.use_card().form_field_element());
- for (const auto& required_field_proto :
- proto_.use_card().required_fields()) {
- required_fields_.emplace_back();
- RequiredField& required_field = required_fields_.back();
- required_field.card_field = required_field_proto.card_field();
- required_field.selector = Selector(required_field_proto.element());
- required_field.simulate_key_presses =
- required_field_proto.simulate_key_presses();
- required_field.delay_in_millisecond =
- required_field_proto.delay_in_millisecond();
- required_field.forced = required_field_proto.forced();
- }
- }
- selector_.MustBeVisible();
- DCHECK(!selector_.empty());
-}
-
-AutofillAction::~AutofillAction() = default;
-
-void AutofillAction::InternalProcessAction(
- ProcessActionCallback action_callback) {
- process_action_callback_ = std::move(action_callback);
-
- // Ensure data already selected in a previous action.
- bool has_valid_data =
- (is_autofill_card_ && delegate_->GetClientMemory()->selected_card()) ||
- (!is_autofill_card_ &&
- delegate_->GetClientMemory()->selected_address(name_));
- if (!has_valid_data) {
- auto* error_info = processed_action_proto_->mutable_status_details()
- ->mutable_autofill_error_info();
- error_info->set_address_key_requested(name_);
- error_info->set_client_memory_address_key_names(
- delegate_->GetClientMemory()->GetAllAddressKeyNames());
- EndAction(PRECONDITION_FAILED);
- return;
- }
-
- FillFormWithData();
-}
-
-void AutofillAction::EndAction(ProcessedActionStatusProto status) {
- EndAction(ClientStatus(status));
-}
-
-void AutofillAction::EndAction(const ClientStatus& status) {
- UpdateProcessedAction(status);
- std::move(process_action_callback_).Run(std::move(processed_action_proto_));
-}
-
-void AutofillAction::FillFormWithData() {
- delegate_->ShortWaitForElement(
- selector_, base::BindOnce(&AutofillAction::OnWaitForElement,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void AutofillAction::OnWaitForElement(bool element_found) {
- if (!element_found) {
- EndAction(ELEMENT_RESOLUTION_FAILED);
- return;
- }
-
- DCHECK(!selector_.empty());
- if (is_autofill_card_) {
- delegate_->GetFullCard(base::BindOnce(&AutofillAction::OnGetFullCard,
- weak_ptr_factory_.GetWeakPtr()));
- return;
- }
-
- DVLOG(3) << "Retrieving address from client memory under '" << name_ << "'.";
- const autofill::AutofillProfile* profile =
- delegate_->GetClientMemory()->selected_address(name_);
- DCHECK(profile);
- auto fallback_data = std::make_unique<FallbackData>();
- fallback_data->profile = profile;
- delegate_->FillAddressForm(
- profile, selector_,
- base::BindOnce(&AutofillAction::OnFormFilled,
- weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
-}
-
-void AutofillAction::OnGetFullCard(std::unique_ptr<autofill::CreditCard> card,
- const base::string16& cvc) {
- if (!card) {
- EndAction(GET_FULL_CARD_FAILED);
- return;
- }
-
- auto fallback_data = std::make_unique<FallbackData>();
- fallback_data->cvc = base::UTF16ToUTF8(cvc);
- fallback_data->expiration_month = card->expiration_month();
- fallback_data->expiration_year = card->expiration_year();
-
- delegate_->FillCardForm(
- std::move(card), cvc, selector_,
- base::BindOnce(&AutofillAction::OnFormFilled,
- weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
-}
-
-void AutofillAction::OnFormFilled(std::unique_ptr<FallbackData> fallback_data,
- const ClientStatus& status) {
- // In case Autofill failed, we fail the action.
- if (!status.ok()) {
- EndAction(status);
- return;
- }
- CheckRequiredFields(std::move(fallback_data));
-}
-
-void AutofillAction::CheckRequiredFields(
- std::unique_ptr<FallbackData> fallback_data) {
- // If there are no required fields, finish the action successfully.
- if (required_fields_.empty()) {
- EndAction(ACTION_APPLIED);
- return;
- }
-
- DCHECK(!batch_element_checker_);
- batch_element_checker_ = std::make_unique<BatchElementChecker>();
- for (size_t i = 0; i < required_fields_.size(); i++) {
- if (required_fields_[i].forced)
- continue;
-
- batch_element_checker_->AddFieldValueCheck(
- required_fields_[i].selector,
- base::BindOnce(&AutofillAction::OnGetRequiredFieldValue,
- weak_ptr_factory_.GetWeakPtr(), i));
- }
- batch_element_checker_->AddAllDoneCallback(
- base::BindOnce(&AutofillAction::OnCheckRequiredFieldsDone,
- weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
- delegate_->RunElementChecks(batch_element_checker_.get());
-}
-
-void AutofillAction::OnGetRequiredFieldValue(size_t required_fields_index,
- bool exists,
- const std::string& value) {
- required_fields_[required_fields_index].status =
- value.empty() ? EMPTY : NOT_EMPTY;
-}
-
-void AutofillAction::OnCheckRequiredFieldsDone(
- std::unique_ptr<FallbackData> fallback_data) {
- batch_element_checker_.reset();
-
- // We process all fields with an empty value in order to perform the fallback
- // on all those fields, if any.
- bool should_fallback = false;
- for (const RequiredField& required_field : required_fields_) {
- if (required_field.ShouldFallback(fallback_data != nullptr)) {
- should_fallback = true;
- break;
- }
- }
-
- if (!should_fallback) {
- EndAction(ACTION_APPLIED);
- return;
- }
-
- if (!fallback_data) {
- // Validation failed and we don't want to try the fallback.
- EndAction(MANUAL_FALLBACK);
- return;
- }
-
- // If there are any fallbacks for the empty fields, set them, otherwise fail
- // immediately.
- bool has_fallbacks = false;
- for (const RequiredField& required_field : required_fields_) {
- if (!required_field.ShouldFallback(/* has_fallback_data= */ true))
- continue;
-
- if (!GetFallbackValue(required_field, *fallback_data).empty()) {
- has_fallbacks = true;
- }
- }
- if (!has_fallbacks) {
- EndAction(MANUAL_FALLBACK);
- return;
- }
-
- // Set the fallback values and check again.
- SetFallbackFieldValuesSequentially(0, std::move(fallback_data));
-}
-
-void AutofillAction::SetFallbackFieldValuesSequentially(
- size_t required_fields_index,
- std::unique_ptr<FallbackData> fallback_data) {
- // Skip non-empty fields.
- while (required_fields_index < required_fields_.size() &&
- !required_fields_[required_fields_index].ShouldFallback(
- fallback_data != nullptr)) {
- required_fields_index++;
- }
-
- // If there are no more fields to set, check the required fields again,
- // but this time we don't want to try the fallback in case of failure.
- if (required_fields_index >= required_fields_.size()) {
- DCHECK_EQ(required_fields_index, required_fields_.size());
-
- CheckRequiredFields(/* fallback_data= */ nullptr);
- return;
- }
-
- // Set the next field to its fallback value.
- const RequiredField& required_field = required_fields_[required_fields_index];
- std::string fallback_value = GetFallbackValue(required_field, *fallback_data);
- if (fallback_value.empty()) {
- DVLOG(3) << "No fallback for " << required_field.selector;
- // If there is no fallback value, we skip this failed field.
- SetFallbackFieldValuesSequentially(++required_fields_index,
- std::move(fallback_data));
- return;
- }
- DVLOG(3) << "Setting fallback value for " << required_field.selector;
-
- delegate_->SetFieldValue(
- required_field.selector, fallback_value,
- required_field.simulate_key_presses, required_field.delay_in_millisecond,
- base::BindOnce(&AutofillAction::OnSetFallbackFieldValue,
- weak_ptr_factory_.GetWeakPtr(), required_fields_index,
- std::move(fallback_data)));
-}
-
-void AutofillAction::OnSetFallbackFieldValue(
- size_t required_fields_index,
- std::unique_ptr<FallbackData> fallback_data,
- const ClientStatus& status) {
- if (!status.ok()) {
- // Fallback failed: we stop the script without checking the fields.
- EndAction(MANUAL_FALLBACK);
- return;
- }
- SetFallbackFieldValuesSequentially(++required_fields_index,
- std::move(fallback_data));
-}
-
-std::string AutofillAction::GetFallbackValue(
- const RequiredField& required_field,
- const FallbackData& fallback_data) {
- if (required_field.address_field !=
- UseAddressProto::RequiredField::UNDEFINED &&
- fallback_data.profile) {
- return base::UTF16ToUTF8(GetAddressFieldValue(
- fallback_data.profile, required_field.address_field));
- }
- if (required_field.card_field !=
- UseCreditCardProto::RequiredField::UNDEFINED) {
- return GetCreditCardFieldValue(required_field.card_field, fallback_data);
- }
- NOTREACHED() << "Unsupported field type for " << required_field.selector;
- return "";
-}
-
-std::string AutofillAction::GetCreditCardFieldValue(
- UseCreditCardProto::RequiredField::CardField field,
- const FallbackData& fallback_data) {
- switch (field) {
- case UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE:
- return fallback_data.cvc;
-
- case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_MONTH:
- if (fallback_data.expiration_month > 0)
- return base::StringPrintf("%02d", fallback_data.expiration_month);
- break;
-
- case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_2_DIGIT_YEAR:
- if (fallback_data.expiration_year > 0)
- return base::StringPrintf("%02d", fallback_data.expiration_year % 100);
- break;
-
- case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_4_DIGIT_YEAR:
- if (fallback_data.expiration_year > 0)
- return base::NumberToString(fallback_data.expiration_year);
- break;
-
- case UseCreditCardProto::RequiredField::UNDEFINED:
- NOTREACHED();
- return "";
- }
- return "";
-}
-
-base::string16 AutofillAction::GetAddressFieldValue(
- const autofill::AutofillProfile* profile,
- const UseAddressProto::RequiredField::AddressField& address_field) {
- // TODO(crbug.com/806868): Get the actual application locale.
- std::string app_locale = "en-US";
- switch (address_field) {
- case UseAddressProto::RequiredField::FIRST_NAME:
- return profile->GetInfo(autofill::NAME_FIRST, app_locale);
- case UseAddressProto::RequiredField::LAST_NAME:
- return profile->GetInfo(autofill::NAME_LAST, app_locale);
- case UseAddressProto::RequiredField::FULL_NAME:
- return profile->GetInfo(autofill::NAME_FULL, app_locale);
- case UseAddressProto::RequiredField::PHONE_NUMBER:
- return profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, app_locale);
- case UseAddressProto::RequiredField::EMAIL:
- return profile->GetInfo(autofill::EMAIL_ADDRESS, app_locale);
- case UseAddressProto::RequiredField::ORGANIZATION:
- return profile->GetInfo(autofill::COMPANY_NAME, app_locale);
- case UseAddressProto::RequiredField::COUNTRY_CODE:
- return profile->GetInfo(autofill::ADDRESS_HOME_COUNTRY, app_locale);
- case UseAddressProto::RequiredField::REGION:
- return profile->GetInfo(autofill::ADDRESS_HOME_STATE, app_locale);
- case UseAddressProto::RequiredField::STREET_ADDRESS:
- return profile->GetInfo(autofill::ADDRESS_HOME_STREET_ADDRESS,
- app_locale);
- case UseAddressProto::RequiredField::LOCALITY:
- return profile->GetInfo(autofill::ADDRESS_HOME_CITY, app_locale);
- case UseAddressProto::RequiredField::DEPENDANT_LOCALITY:
- return profile->GetInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
- app_locale);
- case UseAddressProto::RequiredField::POSTAL_CODE:
- return profile->GetInfo(autofill::ADDRESS_HOME_ZIP, app_locale);
- case UseAddressProto::RequiredField::UNDEFINED:
- NOTREACHED();
- return base::string16();
- }
-}
-} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/autofill_action.h b/chromium/components/autofill_assistant/browser/actions/autofill_action.h
deleted file mode 100644
index 327a55a1c69..00000000000
--- a/chromium/components/autofill_assistant/browser/actions/autofill_action.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_AUTOFILL_ACTION_H_
-#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_AUTOFILL_ACTION_H_
-
-#include <memory>
-#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/batch_element_checker.h"
-
-namespace autofill {
-class AutofillProfile;
-class CreditCard;
-} // namespace autofill
-
-namespace autofill_assistant {
-class ClientStatus;
-
-// An action to autofill a form using a local address or credit card.
-class AutofillAction : public Action {
- public:
- explicit AutofillAction(ActionDelegate* delegate, const ActionProto& proto);
- ~AutofillAction() override;
-
- private:
- enum FieldValueStatus { UNKNOWN, EMPTY, NOT_EMPTY };
- struct RequiredField {
- Selector selector;
- bool simulate_key_presses = false;
- int delay_in_millisecond = 0;
- bool forced = false;
- FieldValueStatus status = UNKNOWN;
-
- // When filling in credit card, card_field must be set. When filling in
- // address, address_field must be set.
- UseCreditCardProto::RequiredField::CardField card_field =
- UseCreditCardProto::RequiredField::UNDEFINED;
- UseAddressProto::RequiredField::AddressField address_field =
- UseAddressProto::RequiredField::UNDEFINED;
-
- // Returns true if fallback is required for this field.
- bool ShouldFallback(bool has_fallback_data) const {
- return status == EMPTY || (forced && has_fallback_data);
- }
- };
-
- // Data necessary for filling in the fallback fields. This is kept in a
- // separate struct to make sure we don't keep it for longer than strictly
- // necessary.
- struct FallbackData {
- FallbackData() = default;
- ~FallbackData() = default;
-
- // Profile for UseAddress fallback.
- const autofill::AutofillProfile* profile = nullptr;
-
- // Card information for UseCreditCard fallback.
- std::string cvc;
- int expiration_year = 0;
- int expiration_month = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FallbackData);
- };
-
- // Overrides Action:
- void InternalProcessAction(ProcessActionCallback callback) override;
-
- void EndAction(ProcessedActionStatusProto status);
- void EndAction(const ClientStatus& status);
-
- // Fill the form using data in client memory. Return whether filling succeeded
- // or not through OnAddressFormFilled or OnCardFormFilled.
- void FillFormWithData();
- void OnWaitForElement(bool element_found);
-
- // Called after getting full credit card with its cvc.
- void OnGetFullCard(std::unique_ptr<autofill::CreditCard> card,
- const base::string16& cvc);
-
- // Called when the form, credit card or address, has been filled.
- void OnFormFilled(std::unique_ptr<FallbackData> fallback_data,
- const ClientStatus& status);
-
- // Check whether all required fields have a non-empty value. If it is the
- // case, finish the action successfully. If it's not and |fallback_data|
- // is null, fail the action. If |fallback_data| is non-null, use it to attempt
- // to fill the failed fields without Autofill.
- void CheckRequiredFields(std::unique_ptr<FallbackData> fallback_data);
-
- // Triggers the check for a specific field.
- void CheckRequiredFieldsSequentially(
- bool allow_fallback,
- size_t required_fields_index,
- std::unique_ptr<FallbackData> fallback_data);
-
- // Updates |required_fields_value_status_|.
- void OnGetRequiredFieldValue(size_t required_fields_index,
- bool exists,
- const std::string& value);
-
- // Called when all required fields have been checked.
- void OnCheckRequiredFieldsDone(std::unique_ptr<FallbackData> fallback_data);
-
- // Gets the fallback value.
- std::string GetFallbackValue(const RequiredField& required_field,
- const FallbackData& fallback_data);
-
- // Gets the value of |field| from |fallback_data|, if available. Returns an
- // empty string otherwise.
- std::string GetCreditCardFieldValue(
- UseCreditCardProto::RequiredField::CardField field,
- const FallbackData& fallback_data);
-
- // Get the value of |address_field| associated to profile |profile|. Return
- // empty string if there is no data available.
- base::string16 GetAddressFieldValue(
- const autofill::AutofillProfile* profile,
- const UseAddressProto::RequiredField::AddressField& address_field);
-
- // Sets fallback field values for empty fields from
- // |required_fields_value_status_|.
- void SetFallbackFieldValuesSequentially(
- size_t required_fields_index,
- std::unique_ptr<FallbackData> fallback_data);
-
- // Called after trying to set form values without Autofill in case of fallback
- // after failed validation.
- void OnSetFallbackFieldValue(size_t required_fields_index,
- std::unique_ptr<FallbackData> fallback_data,
- const ClientStatus& status);
-
- // Usage of the autofilled address. Ignored if autofilling a card.
- std::string name_;
- std::string prompt_;
- Selector selector_;
-
- // True if autofilling a card, otherwise we are autofilling an address.
- bool is_autofill_card_;
- std::vector<RequiredField> required_fields_;
-
- std::unique_ptr<BatchElementChecker> batch_element_checker_;
-
- ProcessActionCallback process_action_callback_;
- base::WeakPtrFactory<AutofillAction> weak_ptr_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(AutofillAction);
-};
-
-} // namespace autofill_assistant
-#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_AUTOFILL_ACTION_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/click_action.cc b/chromium/components/autofill_assistant/browser/actions/click_action.cc
index a26f48cc922..b96e0ed6f93 100644
--- a/chromium/components/autofill_assistant/browser/actions/click_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/click_action.cc
@@ -9,6 +9,7 @@
#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"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -49,9 +50,9 @@ void ClickAction::InternalProcessAction(ProcessActionCallback callback) {
void ClickAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/click_action.h b/chromium/components/autofill_assistant/browser/actions/click_action.h
index 5c2b6afc2fe..ecd21c258a7 100644
--- a/chromium/components/autofill_assistant/browser/actions/click_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/click_action.h
@@ -29,7 +29,7 @@ class ClickAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found);
+ const ClientStatus& element_status);
void OnClick(ProcessActionCallback callback, const ClientStatus& status);
ClickType click_type_;
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 5bf0d0be09f..d29f1a1600f 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -5,6 +5,7 @@
#include "components/autofill_assistant/browser/actions/collect_user_data_action.h"
#include <algorithm>
+#include <set>
#include <utility>
#include "base/android/locale_utils.h"
@@ -32,6 +33,7 @@
namespace {
using autofill_assistant::CollectUserDataOptions;
+using autofill_assistant::DateTimeProto;
using autofill_assistant::TermsAndConditionsState;
bool IsCompleteContact(
const autofill::AutofillProfile* profile,
@@ -81,14 +83,6 @@ bool IsCompleteAddress(const autofill::AutofillProfile* profile,
return true;
}
-bool IsCompleteBillingAddress(
- const autofill::AutofillProfile* profile,
- const CollectUserDataOptions& collect_user_data_options) {
- return !collect_user_data_options.request_payment_method ||
- IsCompleteAddress(
- profile, collect_user_data_options.require_billing_postal_code);
-}
-
bool IsCompleteShippingAddress(
const autofill::AutofillProfile* profile,
const CollectUserDataOptions& collect_user_data_options) {
@@ -97,14 +91,20 @@ bool IsCompleteShippingAddress(
}
bool IsCompleteCreditCard(
- autofill::PersonalDataManager* personal_data_manager,
const autofill::CreditCard* credit_card,
+ const autofill::AutofillProfile* billing_profile,
const CollectUserDataOptions& collect_user_data_options) {
if (!collect_user_data_options.request_payment_method) {
return true;
}
- if (!credit_card) {
+ if (!credit_card || !billing_profile) {
+ return false;
+ }
+
+ if (!IsCompleteAddress(
+ billing_profile,
+ collect_user_data_options.require_billing_postal_code)) {
return false;
}
@@ -120,12 +120,6 @@ bool IsCompleteCreditCard(
return false;
}
- auto* address_profile = personal_data_manager->GetProfileByGUID(
- credit_card->billing_address_id());
- if (!IsCompleteBillingAddress(address_profile, collect_user_data_options)) {
- return false;
- }
-
std::string basic_card_network =
autofill::data_util::GetPaymentRequestData(credit_card->network())
.basic_card_issuer_network;
@@ -153,6 +147,71 @@ bool IsValidTermsChoice(
terms_state != TermsAndConditionsState::NOT_SELECTED;
}
+// Comparison function for |DateTimeProto|.
+// Returns 0 if equal, < 0 if |first| < |second|, > 0 if |second| > |first|.
+int CompareDateTimes(const DateTimeProto& first, const DateTimeProto& second) {
+ auto first_tuple = std::make_tuple(
+ first.date().year(), first.date().month(), first.date().day(),
+ first.time().hour(), first.time().minute(), first.time().second());
+ auto second_tuple = std::make_tuple(
+ second.date().year(), second.date().month(), second.date().day(),
+ second.time().hour(), second.time().minute(), second.time().second());
+ if (first_tuple < second_tuple) {
+ return -1;
+ } else if (second_tuple < first_tuple) {
+ return 1;
+ }
+ return 0;
+}
+
+bool IsValidDateTimeRange(
+ const DateTimeProto& start,
+ const DateTimeProto& end,
+ const CollectUserDataOptions& collect_user_data_options) {
+ return !collect_user_data_options.request_date_time_range ||
+ CompareDateTimes(start, end) < 0;
+}
+
+bool IsValidUserFormSection(
+ const autofill_assistant::UserFormSectionProto& proto) {
+ if (proto.title().empty()) {
+ DVLOG(2) << "UserFormSectionProto: Empty title not allowed.";
+ return false;
+ }
+ switch (proto.section_case()) {
+ case autofill_assistant::UserFormSectionProto::kStaticTextSection:
+ if (proto.static_text_section().text().empty()) {
+ DVLOG(2) << "StaticTextSectionProto: Empty text not allowed.";
+ return false;
+ }
+ break;
+ case autofill_assistant::UserFormSectionProto::kTextInputSection: {
+ if (proto.text_input_section().input_fields().empty()) {
+ DVLOG(2) << "TextInputProto: At least one input must be specified.";
+ return false;
+ }
+ std::set<std::string> memory_keys;
+ for (const auto& input_field :
+ proto.text_input_section().input_fields()) {
+ if (input_field.client_memory_key().empty()) {
+ DVLOG(2) << "TextInputProto: Memory key must be specified.";
+ return false;
+ }
+ if (!memory_keys.insert(input_field.client_memory_key()).second) {
+ DVLOG(2) << "TextInputProto: Duplicate memory keys ("
+ << input_field.client_memory_key() << ").";
+ return false;
+ }
+ }
+ break;
+ }
+ case autofill_assistant::UserFormSectionProto::SECTION_NOT_SET:
+ DVLOG(2) << "UserFormSectionProto: section oneof not set.";
+ return false;
+ }
+ return true;
+}
+
} // namespace
namespace autofill_assistant {
@@ -250,15 +309,19 @@ void CollectUserDataAction::OnGetLogins(
std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
std::vector<WebsiteLoginFetcher::Login> logins) {
for (const auto& login : logins) {
- LoginChoice choice = {
- base::NumberToString(collect_user_data_options->login_choices.size()),
- login.username, login_option.preselection_priority()};
- collect_user_data_options->login_choices.emplace_back(std::move(choice));
+ auto identifier =
+ base::NumberToString(collect_user_data_options->login_choices.size());
+ collect_user_data_options->login_choices.emplace_back(
+ identifier, login.username, login_option.sublabel(),
+ login_option.sublabel_accessibility_hint(),
+ login_option.preselection_priority(),
+ login_option.has_info_popup()
+ ? base::make_optional(login_option.info_popup())
+ : base::nullopt);
login_details_map_.emplace(
- choice.identifier,
- std::make_unique<LoginDetails>(
- login_option.choose_automatically_if_no_other_options(),
- login_option.payload(), login));
+ identifier, std::make_unique<LoginDetails>(
+ login_option.choose_automatically_if_no_other_options(),
+ login_option.payload(), login));
}
ShowToUser(std::move(collect_user_data_options));
}
@@ -279,6 +342,28 @@ void CollectUserDataAction::ShowToUser(
user_data->terms_and_conditions = REQUIRES_REVIEW;
break;
}
+ for (const auto& additional_section :
+ collect_user_data.additional_prepended_sections()) {
+ if (additional_section.section_case() ==
+ UserFormSectionProto::kTextInputSection) {
+ for (const auto& text_input :
+ additional_section.text_input_section().input_fields()) {
+ user_data->additional_values_to_store.emplace(
+ text_input.client_memory_key(), text_input.value());
+ }
+ }
+ }
+ for (const auto& additional_section :
+ collect_user_data.additional_appended_sections()) {
+ if (additional_section.section_case() ==
+ UserFormSectionProto::kTextInputSection) {
+ for (const auto& text_input :
+ additional_section.text_input_section().input_fields()) {
+ user_data->additional_values_to_store.emplace(
+ text_input.client_memory_key(), text_input.value());
+ }
+ }
+ }
if (collect_user_data_options->request_login_choice &&
collect_user_data_options->login_choices.empty()) {
@@ -403,6 +488,18 @@ void CollectUserDataAction::OnGetUserData(
->set_login_payload(login_details->second->payload);
}
+ if (collect_user_data.has_date_time_range()) {
+ *processed_action_proto_->mutable_collect_user_data_result()
+ ->mutable_date_time_start() = user_data->date_time_range_start;
+ *processed_action_proto_->mutable_collect_user_data_result()
+ ->mutable_date_time_end() = user_data->date_time_range_end;
+ }
+
+ for (const auto& value : user_data->additional_values_to_store) {
+ delegate_->GetClientMemory()->set_additional_value(value.first,
+ value.second);
+ }
+
processed_action_proto_->mutable_collect_user_data_result()
->set_is_terms_and_conditions_accepted(
user_data->terms_and_conditions ==
@@ -477,9 +574,14 @@ CollectUserDataAction::CreateOptionsFromProto() {
base::NumberToString(
collect_user_data_options->login_choices.size()),
login_option.custom().label(),
+ login_option.sublabel(),
+ login_option.sublabel_accessibility_hint(),
login_option.has_preselection_priority()
? login_option.preselection_priority()
- : -1};
+ : -1,
+ login_option.has_info_popup()
+ ? base::make_optional(login_option.info_popup())
+ : base::nullopt};
collect_user_data_options->login_choices.emplace_back(
std::move(choice));
login_details_map_.emplace(
@@ -500,6 +602,42 @@ CollectUserDataAction::CreateOptionsFromProto() {
}
}
+ if (collect_user_data.has_date_time_range()) {
+ if (!collect_user_data.date_time_range().has_start_label() ||
+ !collect_user_data.date_time_range().has_end_label() ||
+ !collect_user_data.date_time_range().has_start() ||
+ !collect_user_data.date_time_range().has_end() ||
+ !collect_user_data.date_time_range().has_min() ||
+ !collect_user_data.date_time_range().has_max()) {
+ DVLOG(1) << "Invalid action: missing one or more of the required fields "
+ "'start', 'end', 'min', 'max', 'start_label', end_label'.";
+ return nullptr;
+ }
+ collect_user_data_options->request_date_time_range = true;
+ collect_user_data_options->date_time_range =
+ collect_user_data.date_time_range();
+ }
+
+ for (const auto& section :
+ collect_user_data.additional_prepended_sections()) {
+ if (!IsValidUserFormSection(section)) {
+ DVLOG(1)
+ << "Invalid UserFormSectionProto in additional_prepended_sections";
+ return nullptr;
+ }
+ collect_user_data_options->additional_prepended_sections.emplace_back(
+ section);
+ }
+ for (const auto& section : collect_user_data.additional_appended_sections()) {
+ if (!IsValidUserFormSection(section)) {
+ DVLOG(1)
+ << "Invalid UserFormSectionProto in additional_appended_sections";
+ return nullptr;
+ }
+ collect_user_data_options->additional_appended_sections.emplace_back(
+ section);
+ }
+
// TODO(crbug.com/806868): Maybe we could refactor this to make the confirm
// chip and direct_action part of the additional_actions.
std::string confirm_text = collect_user_data.confirm_button_text();
@@ -521,14 +659,26 @@ CollectUserDataAction::CreateOptionsFromProto() {
if (collect_user_data.request_terms_and_conditions()) {
collect_user_data_options->show_terms_as_checkbox =
collect_user_data.show_terms_as_checkbox();
+
+ if (collect_user_data.accept_terms_and_conditions_text().empty()) {
+ return nullptr;
+ }
collect_user_data_options->accept_terms_and_conditions_text =
collect_user_data.accept_terms_and_conditions_text();
- if (collect_user_data_options->accept_terms_and_conditions_text.empty()) {
- collect_user_data_options->accept_terms_and_conditions_text =
- l10n_util::GetStringUTF8(
- IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_ACCEPT);
+
+ if (!collect_user_data.show_terms_as_checkbox() &&
+ collect_user_data.terms_require_review_text().empty()) {
+ return nullptr;
}
+ collect_user_data_options->terms_require_review_text =
+ collect_user_data.terms_require_review_text();
+ }
+
+ if (collect_user_data.thirdparty_privacy_notice_text().empty()) {
+ return nullptr;
}
+ collect_user_data_options->thirdparty_privacy_notice_text =
+ collect_user_data.thirdparty_privacy_notice_text();
collect_user_data_options->default_email =
delegate_->GetAccountEmailAddress();
@@ -574,8 +724,14 @@ bool CollectUserDataAction::CheckInitialAutofillDataComplete(
credit_cards.begin(), credit_cards.end(),
[&collect_user_data_options,
personal_data_manager](const auto* credit_card) {
- return IsCompleteCreditCard(personal_data_manager, credit_card,
- collect_user_data_options);
+ // TODO(b/142630213): Figure out how to retrieve billing profile if
+ // user has turned off addresses in Chrome settings.
+ return IsCompleteCreditCard(
+ credit_card,
+ credit_card != nullptr
+ ? personal_data_manager->GetProfileByGUID(credit_card->guid())
+ : nullptr,
+ collect_user_data_options);
});
if (completeCardIter == credit_cards.end()) {
return false;
@@ -589,16 +745,16 @@ bool CollectUserDataAction::CheckInitialAutofillDataComplete(
// static
bool CollectUserDataAction::IsUserDataComplete(
- autofill::PersonalDataManager* personal_data_manager,
const UserData& user_data,
const CollectUserDataOptions& options) {
return IsCompleteContact(user_data.contact_profile.get(), options) &&
- IsCompleteBillingAddress(user_data.billing_address.get(), options) &&
IsCompleteShippingAddress(user_data.shipping_address.get(), options) &&
- IsCompleteCreditCard(personal_data_manager, user_data.card.get(),
- options) &&
+ IsCompleteCreditCard(user_data.card.get(),
+ user_data.billing_address.get(), options) &&
IsValidLoginChoice(user_data.login_choice_identifier, options) &&
- IsValidTermsChoice(user_data.terms_and_conditions, options);
+ IsValidTermsChoice(user_data.terms_and_conditions, options) &&
+ IsValidDateTimeRange(user_data.date_time_range_start,
+ user_data.date_time_range_end, options);
}
void CollectUserDataAction::OnPersonalDataChanged() {
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
index b6c9abc520f..86bbf7100d8 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action.h
@@ -33,7 +33,6 @@ class CollectUserDataAction : public Action,
void OnPersonalDataChanged() override;
static bool IsUserDataComplete(
- autofill::PersonalDataManager* personal_data_manager,
const UserData& user_data,
const CollectUserDataOptions& collect_user_data_options);
diff --git a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 166759c831c..60985d3705c 100644
--- a/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -30,6 +30,28 @@ const char kMemoryLocation[] = "billing";
namespace autofill_assistant {
namespace {
+void SetDateTimeProto(DateTimeProto* proto,
+ int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) {
+ proto->mutable_date()->set_year(year);
+ proto->mutable_date()->set_month(month);
+ proto->mutable_date()->set_day(day);
+ proto->mutable_time()->set_hour(hour);
+ proto->mutable_time()->set_minute(minute);
+ proto->mutable_time()->set_second(second);
+}
+
+MATCHER_P(EqualsProto, message, "") {
+ std::string expected_serialized, actual_serialized;
+ message.SerializeToString(&expected_serialized);
+ arg.SerializeToString(&actual_serialized);
+ return expected_serialized == actual_serialized;
+}
+
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::Eq;
@@ -37,6 +59,17 @@ using ::testing::Invoke;
using ::testing::Property;
using ::testing::Return;
+void SetRequiredTermsFields(CollectUserDataProto* data,
+ bool request_terms_and_conditions = false) {
+ data->set_thirdparty_privacy_notice_text("privacy");
+
+ if (request_terms_and_conditions) {
+ data->set_accept_terms_and_conditions_text("terms and conditions");
+ data->set_terms_require_review_text("terms review");
+ }
+ data->set_request_terms_and_conditions(request_terms_and_conditions);
+}
+
class CollectUserDataActionTest : public content::RenderViewHostTestHarness {
public:
void SetUp() override {
@@ -77,20 +110,158 @@ class CollectUserDataActionTest : public content::RenderViewHostTestHarness {
ClientMemory client_memory_;
};
+TEST_F(CollectUserDataActionTest, FailsForMissingPrivacyText) {
+ ActionProto action_proto;
+ action_proto.mutable_collect_user_data();
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, SucceedsForPrivacyTextPresent) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ collect_user_data_proto->set_thirdparty_privacy_notice_text("privacy");
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ user_data->terms_and_conditions = ACCEPTED;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(AllOf(
+ Property(&ProcessedActionProto::status, ACTION_APPLIED),
+ Property(
+ &ProcessedActionProto::collect_user_data_result,
+ Property(
+ &CollectUserDataResultProto::is_terms_and_conditions_accepted,
+ true))))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, FailsForMissingTermsAcceptTextIfRequired) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ collect_user_data_proto->set_thirdparty_privacy_notice_text("privacy");
+ collect_user_data_proto->set_request_terms_and_conditions(true);
+ collect_user_data_proto->set_terms_require_review_text("terms review");
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, FailsForMissingTermsReviewTextIfRequired) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ collect_user_data_proto->set_thirdparty_privacy_notice_text("privacy");
+ collect_user_data_proto->set_request_terms_and_conditions(true);
+ collect_user_data_proto->set_accept_terms_and_conditions_text(
+ "terms and conditions");
+ collect_user_data_proto->set_show_terms_as_checkbox(false);
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, SucceedsForCheckboxIfReviewTextMissing) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ collect_user_data_proto->set_thirdparty_privacy_notice_text("privacy");
+ collect_user_data_proto->set_request_terms_and_conditions(true);
+ collect_user_data_proto->set_accept_terms_and_conditions_text(
+ "terms and conditions");
+ collect_user_data_proto->set_show_terms_as_checkbox(true);
+
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ user_data->terms_and_conditions = ACCEPTED;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(AllOf(
+ Property(&ProcessedActionProto::status, ACTION_APPLIED),
+ Property(
+ &ProcessedActionProto::collect_user_data_result,
+ Property(
+ &CollectUserDataResultProto::is_terms_and_conditions_accepted,
+ true))))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, SucceedsForAllTermsTextPresent) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ collect_user_data_proto->set_thirdparty_privacy_notice_text("privacy");
+ collect_user_data_proto->set_request_terms_and_conditions(true);
+ collect_user_data_proto->set_accept_terms_and_conditions_text(
+ "terms and conditions");
+ collect_user_data_proto->set_show_terms_as_checkbox(false);
+ collect_user_data_proto->set_terms_require_review_text("terms review");
+
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ user_data->terms_and_conditions = ACCEPTED;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(AllOf(
+ Property(&ProcessedActionProto::status, ACTION_APPLIED),
+ Property(
+ &ProcessedActionProto::collect_user_data_result,
+ Property(
+ &CollectUserDataResultProto::is_terms_and_conditions_accepted,
+ true))))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
TEST_F(CollectUserDataActionTest, PromptIsShown) {
const char kPrompt[] = "Some message.";
ActionProto action_proto;
+ SetRequiredTermsFields(action_proto.mutable_collect_user_data());
action_proto.mutable_collect_user_data()->set_prompt(kPrompt);
- CollectUserDataAction action(&mock_action_delegate_, action_proto);
EXPECT_CALL(mock_action_delegate_, SetStatusMessage(kPrompt));
EXPECT_CALL(callback_, Run(_));
+
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
action.ProcessAction(callback_.Get());
}
TEST_F(CollectUserDataActionTest, SelectLogin) {
ActionProto action_proto;
+ SetRequiredTermsFields(action_proto.mutable_collect_user_data());
auto* login_details =
action_proto.mutable_collect_user_data()->mutable_login_details();
auto* login_option = login_details->add_login_options();
@@ -126,9 +297,10 @@ TEST_F(CollectUserDataActionTest, SelectLogin) {
TEST_F(CollectUserDataActionTest, LoginChoiceAutomaticIfNoOtherOptions) {
ActionProto action_proto;
- auto* collect_user_data = action_proto.mutable_collect_user_data();
- collect_user_data->set_request_terms_and_conditions(false);
- auto* login_details = collect_user_data->mutable_login_details();
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+ auto* login_details = collect_user_data_proto->mutable_login_details();
auto* login_option = login_details->add_login_options();
login_option->mutable_custom()->set_label("Guest Checkout");
login_option->set_payload("guest");
@@ -154,8 +326,9 @@ TEST_F(CollectUserDataActionTest, LoginChoiceAutomaticIfNoOtherOptions) {
TEST_F(CollectUserDataActionTest, SelectLoginFailsIfNoOptionAvailable) {
ActionProto action_proto;
- auto* collect_user_data = action_proto.mutable_collect_user_data();
- auto* login_details = collect_user_data->mutable_login_details();
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ auto* login_details = collect_user_data_proto->mutable_login_details();
auto* login_option = login_details->add_login_options();
login_option->mutable_password_manager();
login_option->set_payload("password_manager");
@@ -173,6 +346,7 @@ TEST_F(CollectUserDataActionTest, SelectLoginFailsIfNoOptionAvailable) {
TEST_F(CollectUserDataActionTest, SelectContactDetails) {
ActionProto action_proto;
auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
collect_user_data_proto->set_request_terms_and_conditions(false);
auto* contact_details_proto =
collect_user_data_proto->mutable_contact_details();
@@ -228,6 +402,7 @@ TEST_F(CollectUserDataActionTest, SelectContactDetails) {
TEST_F(CollectUserDataActionTest, SelectPaymentMethod) {
ActionProto action_proto;
+ SetRequiredTermsFields(action_proto.mutable_collect_user_data());
action_proto.mutable_collect_user_data()->set_request_payment_method(true);
action_proto.mutable_collect_user_data()->set_request_terms_and_conditions(
false);
@@ -237,8 +412,6 @@ TEST_F(CollectUserDataActionTest, SelectPaymentMethod) {
"Morrison", "marion@me.xyz", "Fox",
"123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "16505678910");
- ON_CALL(mock_personal_data_manager_, GetProfileByGUID(billing_profile.guid()))
- .WillByDefault(Return(&billing_profile));
autofill::CreditCard credit_card(base::GenerateGUID(), kFakeUrl);
autofill::test::SetCreditCardInfo(&credit_card, "Marion Mitchell",
@@ -251,6 +424,8 @@ TEST_F(CollectUserDataActionTest, SelectPaymentMethod) {
std::unique_ptr<UserData> user_data) {
user_data->card =
std::make_unique<autofill::CreditCard>(credit_card);
+ user_data->billing_address =
+ std::make_unique<autofill::AutofillProfile>(billing_profile);
user_data->succeed = true;
std::move(collect_user_data_options->confirm_callback)
.Run(std::move(user_data));
@@ -286,6 +461,7 @@ TEST_F(CollectUserDataActionTest, MandatoryPostalCodeWithoutErrorMessageFails) {
TEST_F(CollectUserDataActionTest, ContactDetailsCanHandleUtf8) {
ActionProto action_proto;
auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
collect_user_data_proto->set_request_terms_and_conditions(false);
auto* contact_details_proto =
collect_user_data_proto->mutable_contact_details();
@@ -336,39 +512,32 @@ TEST_F(CollectUserDataActionTest, ContactDetailsCanHandleUtf8) {
TEST_F(CollectUserDataActionTest, UserDataComplete_Contact) {
UserData user_data;
CollectUserDataOptions options;
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.contact_profile = std::make_unique<autofill::AutofillProfile>(
base::GenerateGUID(), kFakeUrl);
options.request_payer_email = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.contact_profile->SetRawInfo(
autofill::ServerFieldType::EMAIL_ADDRESS,
base::UTF8ToUTF16("joedoe@example.com"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
options.request_payer_name = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.contact_profile->SetRawInfo(autofill::ServerFieldType::NAME_FULL,
base::UTF8ToUTF16("Joe Doe"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
options.request_payer_phone = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.contact_profile->SetRawInfo(
autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
base::UTF8ToUTF16("+1 23 456 789 01"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
}
TEST_F(CollectUserDataActionTest, UserDataComplete_Payment) {
@@ -376,8 +545,7 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_Payment) {
CollectUserDataOptions options;
options.request_payment_method = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
// Valid credit card, but no billing address.
user_data.card =
@@ -385,15 +553,7 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_Payment) {
autofill::test::SetCreditCardInfo(user_data.card.get(), "Marion Mitchell",
"4111 1111 1111 1111", "01", "2020",
/* billing_address_id = */ "");
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
-
- // Valid credit card, but invalid billing address.
- user_data.card->set_billing_address_id("invalid");
- ON_CALL(mock_personal_data_manager_, GetProfileByGUID("invalid"))
- .WillByDefault(Return(nullptr));
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
// Incomplete billing address.
user_data.billing_address = std::make_unique<autofill::AutofillProfile>(
@@ -402,70 +562,56 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_Payment) {
"Mitchell", "Morrison", "marion@me.xyz", "Fox",
"123 Zoo St.", "unit 5", "Hollywood", "CA",
/* zipcode = */ "", "US", "16505678910");
- ON_CALL(mock_personal_data_manager_,
- GetProfileByGUID(user_data.billing_address->guid()))
- .WillByDefault(Return(user_data.billing_address.get()));
user_data.card->set_billing_address_id(user_data.billing_address->guid());
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.billing_address->SetRawInfo(autofill::ADDRESS_HOME_ZIP,
base::UTF8ToUTF16("91601"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
// Zip code is optional in Argentinian address.
user_data.billing_address->SetRawInfo(autofill::ADDRESS_HOME_ZIP,
base::UTF8ToUTF16(""));
user_data.billing_address->SetRawInfo(autofill::ADDRESS_HOME_COUNTRY,
base::UTF8ToUTF16("AR"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
options.require_billing_postal_code = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.billing_address->SetRawInfo(autofill::ADDRESS_HOME_ZIP,
base::UTF8ToUTF16("B1675"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
}
TEST_F(CollectUserDataActionTest, UserDataComplete_Terms) {
UserData user_data;
CollectUserDataOptions options;
options.accept_terms_and_conditions_text.assign("Accept T&C");
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.terms_and_conditions = REQUIRES_REVIEW;
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.terms_and_conditions = ACCEPTED;
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
}
TEST_F(CollectUserDataActionTest, UserDataComplete_Login) {
UserData user_data;
CollectUserDataOptions options;
options.request_login_choice = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.login_choice_identifier.assign("1");
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
}
TEST_F(CollectUserDataActionTest, UserDataComplete_ShippingAddress) {
UserData user_data;
CollectUserDataOptions options;
options.request_shipping = true;
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
// Incomplete address.
user_data.shipping_address = std::make_unique<autofill::AutofillProfile>(
@@ -474,13 +620,240 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_ShippingAddress) {
"Mitchell", "Morrison", "marion@me.xyz", "Fox",
"123 Zoo St.", "unit 5", "Hollywood", "CA",
/* zipcode = */ "", "US", "16505678910");
- EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
user_data.shipping_address->SetRawInfo(autofill::ADDRESS_HOME_ZIP,
base::UTF8ToUTF16("91601"));
- EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
- &mock_personal_data_manager_, user_data, options));
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
+}
+
+TEST_F(CollectUserDataActionTest, UserDataComplete_DateTimeRange) {
+ UserData user_data;
+ CollectUserDataOptions options;
+ options.request_date_time_range = true;
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
+
+ SetDateTimeProto(&user_data.date_time_range_start, 2019, 12, 31, 10, 30, 0);
+ SetDateTimeProto(&user_data.date_time_range_end, 2019, 1, 28, 16, 0, 0);
+
+ // Start date not before end date.
+ EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, options));
+
+ user_data.date_time_range_end.mutable_date()->set_year(2020);
+ EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, options));
+}
+
+TEST_F(CollectUserDataActionTest, SelectDateTimeRange) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+ auto* date_time_proto = collect_user_data_proto->mutable_date_time_range();
+ SetDateTimeProto(date_time_proto->mutable_start(), 2019, 10, 21, 8, 0, 0);
+ SetDateTimeProto(date_time_proto->mutable_end(), 2019, 11, 5, 16, 0, 0);
+ SetDateTimeProto(date_time_proto->mutable_min(), 2019, 11, 5, 16, 0, 0);
+ SetDateTimeProto(date_time_proto->mutable_max(), 2020, 11, 5, 16, 0, 0);
+ date_time_proto->set_start_label("Pick up");
+ date_time_proto->set_end_label("Return");
+
+ DateTimeProto actual_pickup_time;
+ DateTimeProto actual_return_time;
+ SetDateTimeProto(&actual_pickup_time, 2019, 10, 21, 7, 0, 0);
+ SetDateTimeProto(&actual_return_time, 2019, 10, 25, 19, 0, 0);
+
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [&](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ user_data->date_time_range_start = actual_pickup_time;
+ user_data->date_time_range_end = actual_return_time;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(
+ AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
+ Property(&ProcessedActionProto::collect_user_data_result,
+ Property(&CollectUserDataResultProto::date_time_start,
+ EqualsProto(actual_pickup_time))),
+ Property(&ProcessedActionProto::collect_user_data_result,
+ Property(&CollectUserDataResultProto::date_time_end,
+ EqualsProto(actual_return_time)))))));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+}
+
+TEST_F(CollectUserDataActionTest, StaticSectionValid) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ auto* static_section =
+ collect_user_data_proto->add_additional_prepended_sections();
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ static_section->set_title("Static section");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ static_section->mutable_static_text_section()->set_text("Lorem ipsum.");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ action.ProcessAction(callback_.Get());
+ }
+}
+
+TEST_F(CollectUserDataActionTest, TextInputSectionValid) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ auto* text_input_section =
+ collect_user_data_proto->add_additional_prepended_sections();
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ text_input_section->set_title("Text input section");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ auto* input_field =
+ text_input_section->mutable_text_input_section()->add_input_fields();
+ input_field->set_value("12345");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ input_field->set_input_type(TextInputProto::INPUT_ALPHANUMERIC);
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ input_field->set_client_memory_key("code");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ // Duplicate input field fails due to duplicate memory key.
+ *text_input_section->mutable_text_input_section()->add_input_fields() =
+ *input_field;
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
+ action.ProcessAction(callback_.Get());
+ }
+
+ text_input_section->mutable_text_input_section()
+ ->mutable_input_fields(1)
+ ->set_client_memory_key("something else");
+ {
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ action.ProcessAction(callback_.Get());
+ }
+}
+
+TEST_F(CollectUserDataActionTest, TextInputSectionWritesToClientMemory) {
+ ActionProto action_proto;
+ auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
+ SetRequiredTermsFields(collect_user_data_proto);
+ collect_user_data_proto->set_request_terms_and_conditions(false);
+ ON_CALL(mock_action_delegate_, CollectUserData(_, _))
+ .WillByDefault(Invoke(
+ [](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
+ std::unique_ptr<UserData> user_data) {
+ user_data->succeed = true;
+ user_data->additional_values_to_store["key2"] = "modified";
+ std::move(collect_user_data_options->confirm_callback)
+ .Run(std::move(user_data));
+ }));
+
+ auto* text_input_section =
+ collect_user_data_proto->add_additional_prepended_sections();
+ text_input_section->set_title("Text input section");
+
+ auto* input_field_1 =
+ text_input_section->mutable_text_input_section()->add_input_fields();
+ input_field_1->set_value("initial");
+ input_field_1->set_input_type(TextInputProto::INPUT_ALPHANUMERIC);
+ input_field_1->set_client_memory_key("key1");
+
+ auto* input_field_2 =
+ text_input_section->mutable_text_input_section()->add_input_fields();
+ input_field_2->set_value("initial");
+ input_field_2->set_input_type(TextInputProto::INPUT_ALPHANUMERIC);
+ input_field_2->set_client_memory_key("key2");
+
+ EXPECT_FALSE(client_memory_.has_additional_value("key1"));
+ EXPECT_FALSE(client_memory_.has_additional_value("key2"));
+ CollectUserDataAction action(&mock_action_delegate_, action_proto);
+ EXPECT_CALL(
+ callback_,
+ Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+ action.ProcessAction(callback_.Get());
+ EXPECT_EQ(*client_memory_.additional_value("key1"), "initial");
+ EXPECT_EQ(*client_memory_.additional_value("key2"), "modified");
}
} // namespace
diff --git a/chromium/components/autofill_assistant/browser/actions/focus_element_action.cc b/chromium/components/autofill_assistant/browser/actions/focus_element_action.cc
index 44efa8d0398..8d3b3d2e59a 100644
--- a/chromium/components/autofill_assistant/browser/actions/focus_element_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/focus_element_action.cc
@@ -10,6 +10,7 @@
#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"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -61,9 +62,9 @@ void FocusElementAction::InternalProcessAction(ProcessActionCallback callback) {
void FocusElementAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
const TopPadding& top_padding,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/focus_element_action.h b/chromium/components/autofill_assistant/browser/actions/focus_element_action.h
index 145f692ae50..cc7b665509c 100644
--- a/chromium/components/autofill_assistant/browser/actions/focus_element_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/focus_element_action.h
@@ -27,7 +27,7 @@ class FocusElementAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
const TopPadding& top_padding,
- bool element_found);
+ const ClientStatus& element_status);
void OnFocusElement(ProcessActionCallback callback,
const ClientStatus& status);
diff --git a/chromium/components/autofill_assistant/browser/actions/highlight_element_action.cc b/chromium/components/autofill_assistant/browser/actions/highlight_element_action.cc
index 5c549006ef3..765bcd74f10 100644
--- a/chromium/components/autofill_assistant/browser/actions/highlight_element_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/highlight_element_action.cc
@@ -9,6 +9,7 @@
#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 {
@@ -36,11 +37,12 @@ void HighlightElementAction::InternalProcessAction(
std::move(callback), selector));
}
-void HighlightElementAction::OnWaitForElement(ProcessActionCallback callback,
- const Selector& selector,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+void HighlightElementAction::OnWaitForElement(
+ ProcessActionCallback callback,
+ const Selector& selector,
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/highlight_element_action.h b/chromium/components/autofill_assistant/browser/actions/highlight_element_action.h
index 9b0830079c5..6720f31acd9 100644
--- a/chromium/components/autofill_assistant/browser/actions/highlight_element_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/highlight_element_action.h
@@ -29,7 +29,7 @@ class HighlightElementAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found);
+ const ClientStatus& element_status);
void OnHighlightElement(ProcessActionCallback callback,
const ClientStatus& status);
diff --git a/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h b/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
index c708be319e1..d2271c8cf39 100644
--- a/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/chromium/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -28,31 +28,33 @@ class MockActionDelegate : public ActionDelegate {
MOCK_METHOD1(RunElementChecks, void(BatchElementChecker*));
- void ShortWaitForElement(const Selector& selector,
- base::OnceCallback<void(bool)> callback) override {
+ void ShortWaitForElement(
+ const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&)> callback) override {
OnShortWaitForElement(selector, callback);
}
MOCK_METHOD2(OnShortWaitForElement,
- void(const Selector& selector, base::OnceCallback<void(bool)>&));
+ void(const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&)>&));
void WaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
- check_elements,
- base::OnceCallback<void(ProcessedActionStatusProto)> callback) override {
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements,
+ base::OnceCallback<void(const ClientStatus&)> callback) override {
OnWaitForDom(max_wait_time, allow_interrupt, check_elements, callback);
}
- MOCK_METHOD4(
- OnWaitForDom,
- void(base::TimeDelta,
- bool,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>&,
- base::OnceCallback<void(ProcessedActionStatusProto)>&));
+ MOCK_METHOD4(OnWaitForDom,
+ void(base::TimeDelta,
+ bool,
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)>&,
+ base::OnceCallback<void(const ClientStatus&)>&));
MOCK_METHOD1(SetStatusMessage, void(const std::string& message));
MOCK_METHOD0(GetStatusMessage, std::string());
@@ -131,16 +133,16 @@ class MockActionDelegate : public ActionDelegate {
OnGetFullCard(transformed_callback);
}
- void GetFieldValue(
- const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) {
+ void GetFieldValue(const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&,
+ const std::string&)> callback) {
OnGetFieldValue(selector, callback);
}
- MOCK_METHOD2(
- OnGetFieldValue,
- void(const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)>& callback));
+ MOCK_METHOD2(OnGetFieldValue,
+ void(const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&,
+ const std::string&)>& callback));
void SetFieldValue(const Selector& selector,
const std::string& value,
diff --git a/chromium/components/autofill_assistant/browser/actions/prompt_action.cc b/chromium/components/autofill_assistant/browser/actions/prompt_action.cc
index 4ac8e2c68ea..9de12bce86d 100644
--- a/chromium/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -152,8 +152,10 @@ void PromptAction::CheckAutoSelect() {
delegate_->RunElementChecks(auto_select_checker_.get());
}
-void PromptAction::OnAutoSelectElementExists(int choice_index, bool exists) {
- if (exists)
+void PromptAction::OnAutoSelectElementExists(
+ int choice_index,
+ const ClientStatus& element_status) {
+ if (element_status.ok())
auto_select_choice_index_ = choice_index;
// Calling OnSuggestionChosen() is delayed until try_done, as it indirectly
diff --git a/chromium/components/autofill_assistant/browser/actions/prompt_action.h b/chromium/components/autofill_assistant/browser/actions/prompt_action.h
index f166292890e..b93e0e72576 100644
--- a/chromium/components/autofill_assistant/browser/actions/prompt_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/prompt_action.h
@@ -40,7 +40,8 @@ class PromptAction : public Action {
void UpdateUserActions();
bool HasAutoSelect();
void CheckAutoSelect();
- void OnAutoSelectElementExists(int choice_index, bool exists);
+ void OnAutoSelectElementExists(int choice_index,
+ const ClientStatus& element_status);
void OnAutoSelectDone();
void OnSuggestionChosen(int choice_index);
diff --git a/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index 009a4513f1f..cb81c388ff8 100644
--- a/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -37,9 +37,9 @@ class PromptActionTest : public testing::Test {
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(_, _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(false, ""));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus(), ""));
ON_CALL(mock_action_delegate_, RunElementChecks)
.WillByDefault(Invoke([this](BatchElementChecker* checker) {
@@ -146,13 +146,13 @@ TEST_F(PromptActionTest, ShowOnlyIfElementExists) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
ASSERT_THAT(user_actions_, Pointee(IsEmpty()));
}
@@ -170,14 +170,14 @@ TEST_F(PromptActionTest, DisabledUnlessElementExists) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
EXPECT_TRUE((*user_actions_)[0].enabled());
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
task_env_.FastForwardBy(base::TimeDelta::FromSeconds(1));
ASSERT_THAT(user_actions_, Pointee(SizeIs(1)));
EXPECT_FALSE((*user_actions_)[0].enabled());
@@ -196,7 +196,7 @@ TEST_F(PromptActionTest, AutoSelect) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_action_delegate_, CancelPrompt());
EXPECT_CALL(
@@ -226,7 +226,7 @@ TEST_F(PromptActionTest, AutoSelectWithButton) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(
callback_,
Run(Pointee(AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
diff --git a/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.cc b/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.cc
new file mode 100644
index 00000000000..dff26ef11af
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.cc
@@ -0,0 +1,186 @@
+// 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/required_fields_fallback_handler.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/optional.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "components/autofill_assistant/browser/batch_element_checker.h"
+#include "components/autofill_assistant/browser/client_status.h"
+
+namespace autofill_assistant {
+
+RequiredFieldsFallbackHandler::RequiredFieldsFallbackHandler(
+ const std::vector<RequiredField>& required_fields,
+ base::RepeatingCallback<std::string(const RequiredField&,
+ const FallbackData&)>
+ field_value_getter,
+ base::OnceCallback<void(const ClientStatus&,
+ const base::Optional<ClientStatus>&)>
+ status_update_callback,
+ ActionDelegate* action_delegate) {
+ required_fields_.assign(required_fields.begin(), required_fields.end());
+ field_value_getter_ = std::move(field_value_getter);
+ status_update_callback_ = std::move(status_update_callback);
+ action_delegate_ = action_delegate;
+}
+
+RequiredFieldsFallbackHandler::~RequiredFieldsFallbackHandler() {}
+
+RequiredFieldsFallbackHandler::FallbackData::FallbackData() {}
+
+void RequiredFieldsFallbackHandler::CheckAndFallbackRequiredFields(
+ const ClientStatus& initial_autofill_status,
+ std::unique_ptr<FallbackData> fallback_data) {
+ initial_autofill_status_ = initial_autofill_status;
+
+ if (required_fields_.empty()) {
+ if (!initial_autofill_status.ok()) {
+ DVLOG(1) << __func__ << " Autofill failed and no fallback provided "
+ << initial_autofill_status.proto_status();
+ }
+
+ std::move(status_update_callback_)
+ .Run(initial_autofill_status, base::nullopt);
+ return;
+ }
+
+ CheckAllRequiredFields(std::move(fallback_data));
+}
+
+void RequiredFieldsFallbackHandler::CheckAllRequiredFields(
+ std::unique_ptr<FallbackData> fallback_data) {
+ DCHECK(!batch_element_checker_);
+ batch_element_checker_ = std::make_unique<BatchElementChecker>();
+ for (size_t i = 0; i < required_fields_.size(); i++) {
+ if (required_fields_[i].forced) {
+ continue;
+ }
+
+ batch_element_checker_->AddFieldValueCheck(
+ required_fields_[i].selector,
+ base::BindOnce(&RequiredFieldsFallbackHandler::OnGetRequiredFieldValue,
+ weak_ptr_factory_.GetWeakPtr(), i));
+ }
+
+ batch_element_checker_->AddAllDoneCallback(
+ base::BindOnce(&RequiredFieldsFallbackHandler::OnCheckRequiredFieldsDone,
+ weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
+ action_delegate_->RunElementChecks(batch_element_checker_.get());
+}
+
+void RequiredFieldsFallbackHandler::OnGetRequiredFieldValue(
+ size_t required_fields_index,
+ const ClientStatus& element_status,
+ const std::string& value) {
+ required_fields_[required_fields_index].status =
+ value.empty() ? EMPTY : NOT_EMPTY;
+}
+
+void RequiredFieldsFallbackHandler::OnCheckRequiredFieldsDone(
+ std::unique_ptr<FallbackData> fallback_data) {
+ batch_element_checker_.reset();
+
+ // We process all fields with an empty value in order to perform the fallback
+ // on all those fields, if any.
+ bool should_fallback = false;
+ for (const RequiredField& required_field : required_fields_) {
+ if (required_field.ShouldFallback(fallback_data != nullptr)) {
+ should_fallback = true;
+ break;
+ }
+ }
+
+ if (!should_fallback) {
+ std::move(status_update_callback_)
+ .Run(ClientStatus(ACTION_APPLIED), initial_autofill_status_);
+ return;
+ }
+
+ if (!fallback_data) {
+ // Validation failed and we don't want to try the fallback.
+ std::move(status_update_callback_)
+ .Run(ClientStatus(MANUAL_FALLBACK), initial_autofill_status_);
+ return;
+ }
+
+ // If there are any fallbacks for the empty fields, set them, otherwise fail
+ // immediately.
+ bool has_fallbacks = false;
+ for (const RequiredField& required_field : required_fields_) {
+ if (!required_field.ShouldFallback(/* has_fallback_data= */ true))
+ continue;
+
+ if (!field_value_getter_.Run(required_field, *fallback_data).empty()) {
+ has_fallbacks = true;
+ }
+ }
+ if (!has_fallbacks) {
+ std::move(status_update_callback_)
+ .Run(ClientStatus(MANUAL_FALLBACK), initial_autofill_status_);
+ return;
+ }
+
+ // Set the fallback values and check again.
+ SetFallbackFieldValuesSequentially(0, std::move(fallback_data));
+}
+
+void RequiredFieldsFallbackHandler::SetFallbackFieldValuesSequentially(
+ size_t required_fields_index,
+ std::unique_ptr<FallbackData> fallback_data) {
+ // Skip non-empty fields.
+ while (required_fields_index < required_fields_.size() &&
+ !required_fields_[required_fields_index].ShouldFallback(
+ fallback_data != nullptr)) {
+ required_fields_index++;
+ }
+
+ // If there are no more fields to set, check the required fields again,
+ // but this time we don't want to try the fallback in case of failure.
+ if (required_fields_index >= required_fields_.size()) {
+ DCHECK_EQ(required_fields_index, required_fields_.size());
+
+ return CheckAllRequiredFields(/* fallback_data= */ nullptr);
+ }
+
+ // Set the next field to its fallback value.
+ const RequiredField& required_field = required_fields_[required_fields_index];
+ std::string fallback_value =
+ field_value_getter_.Run(required_field, *fallback_data);
+ if (fallback_value.empty()) {
+ DVLOG(3) << "No fallback for " << required_field.selector;
+ // If there is no fallback value, we skip this failed field.
+ return SetFallbackFieldValuesSequentially(++required_fields_index,
+ std::move(fallback_data));
+ }
+ DVLOG(3) << "Setting fallback value for " << required_field.selector;
+
+ action_delegate_->SetFieldValue(
+ required_field.selector, fallback_value,
+ required_field.simulate_key_presses, required_field.delay_in_millisecond,
+ base::BindOnce(&RequiredFieldsFallbackHandler::OnSetFallbackFieldValue,
+ weak_ptr_factory_.GetWeakPtr(), required_fields_index,
+ std::move(fallback_data)));
+}
+
+void RequiredFieldsFallbackHandler::OnSetFallbackFieldValue(
+ size_t required_fields_index,
+ std::unique_ptr<FallbackData> fallback_data,
+ const ClientStatus& setFieldStatus) {
+ if (!setFieldStatus.ok()) {
+ // Fallback failed: we stop the script without checking the fields.
+ std::move(status_update_callback_)
+ .Run(ClientStatus(MANUAL_FALLBACK), initial_autofill_status_);
+ return;
+ }
+
+ SetFallbackFieldValuesSequentially(++required_fields_index,
+ std::move(fallback_data));
+}
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.h b/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.h
new file mode 100644
index 00000000000..5759c34cfa2
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/required_fields_fallback_handler.h
@@ -0,0 +1,142 @@
+// 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_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/batch_element_checker.h"
+
+namespace autofill {
+class AutofillProfile;
+} // namespace autofill
+
+namespace autofill_assistant {
+class ClientStatus;
+
+// A handler for required fields and fallback values, used for example in
+// UseAddressAction.
+class RequiredFieldsFallbackHandler {
+ public:
+ enum FieldValueStatus { UNKNOWN, EMPTY, NOT_EMPTY };
+ struct RequiredField {
+ Selector selector;
+ bool simulate_key_presses = false;
+ int delay_in_millisecond = 0;
+ bool forced = false;
+ FieldValueStatus status = UNKNOWN;
+
+ // When filling in credit card, card_field must be set. When filling in
+ // address, address_field must be set.
+ UseAddressProto::RequiredField::AddressField address_field =
+ UseAddressProto::RequiredField::UNDEFINED;
+ UseCreditCardProto::RequiredField::CardField card_field =
+ UseCreditCardProto::RequiredField::UNDEFINED;
+
+ // Returns true if fallback is required for this field.
+ bool ShouldFallback(bool has_fallback_data) const {
+ return status == EMPTY || (forced && has_fallback_data);
+ }
+ };
+
+ // Data necessary for filling in the fallback fields. This is kept in a
+ // separate struct to make sure we don't keep it for longer than strictly
+ // necessary.
+ // TODO(marianfe): Refactor this to use a map instead.
+ struct FallbackData {
+ FallbackData();
+ ~FallbackData() = default;
+
+ // autofill profile.
+ const autofill::AutofillProfile* profile;
+
+ // Card information for UseCreditCard fallback.
+ std::string cvc;
+ std::string card_holder_name;
+ std::string card_number;
+ int expiration_year = 0;
+ int expiration_month = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FallbackData);
+ };
+
+ explicit RequiredFieldsFallbackHandler(
+ const std::vector<RequiredField>& required_fields,
+ base::RepeatingCallback<std::string(const RequiredField&,
+ const FallbackData&)>
+ field_value_getter,
+ base::OnceCallback<void(const ClientStatus&,
+ const base::Optional<ClientStatus>&)>
+ status_update_callback,
+ ActionDelegate* delegate);
+ ~RequiredFieldsFallbackHandler();
+
+ // Check if there are required fields. If so, verify them and fallback if
+ // they are empty. If not, update the status to the result of the autofill
+ // action.
+ void CheckAndFallbackRequiredFields(
+ const ClientStatus& initial_autofill_status,
+ std::unique_ptr<FallbackData> fallback_data);
+
+ private:
+ // Check whether all required fields have a non-empty value. If it is the
+ // case, update the status to success. If it's not and |fallback_data|
+ // is null, update the status to failure. If |fallback_data| is non-null, use
+ // it to attempt to fill the failed fields without Autofill.
+ void CheckAllRequiredFields(std::unique_ptr<FallbackData> fallback_data);
+
+ // Triggers the check for a specific field.
+ void CheckRequiredFieldsSequentially(
+ bool allow_fallback,
+ size_t required_fields_index,
+ std::unique_ptr<FallbackData> fallback_data);
+
+ // Updates the status of the required field.
+ void OnGetRequiredFieldValue(size_t required_fields_index,
+ const ClientStatus& element_status,
+ const std::string& value);
+
+ // Called when all required fields have been checked.
+ void OnCheckRequiredFieldsDone(std::unique_ptr<FallbackData> fallback_data);
+
+ // Sets fallback field values for empty fields.
+ void SetFallbackFieldValuesSequentially(
+ size_t required_fields_index,
+ std::unique_ptr<FallbackData> fallback_data);
+
+ // Called after trying to set form values without Autofill in case of fallback
+ // after failed validation.
+ void OnSetFallbackFieldValue(size_t required_fields_index,
+ std::unique_ptr<FallbackData> fallback_data,
+ const ClientStatus& status);
+
+ ClientStatus initial_autofill_status_;
+
+ std::vector<RequiredField> required_fields_;
+ base::RepeatingCallback<std::string(const RequiredField&,
+ const FallbackData&)>
+ field_value_getter_;
+ base::OnceCallback<void(const ClientStatus&,
+ const base::Optional<ClientStatus>&)>
+ status_update_callback_;
+ ActionDelegate* action_delegate_;
+ std::unique_ptr<BatchElementChecker> batch_element_checker_;
+ base::WeakPtrFactory<RequiredFieldsFallbackHandler> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(RequiredFieldsFallbackHandler);
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/select_option_action.cc b/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
index 3c9b96ffd36..f488fa63150 100644
--- a/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/select_option_action.cc
@@ -9,6 +9,7 @@
#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 {
@@ -45,9 +46,9 @@ void SelectOptionAction::InternalProcessAction(ProcessActionCallback callback) {
void SelectOptionAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/select_option_action.h b/chromium/components/autofill_assistant/browser/actions/select_option_action.h
index 7da3615866f..ca632c4fbec 100644
--- a/chromium/components/autofill_assistant/browser/actions/select_option_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/select_option_action.h
@@ -27,7 +27,7 @@ class SelectOptionAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found);
+ const ClientStatus& element_status);
void OnSelectOption(ProcessActionCallback callback,
const ClientStatus& status);
diff --git a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
index 7e72315358c..42733cb9fe0 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.cc
@@ -9,6 +9,7 @@
#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 {
@@ -37,9 +38,9 @@ void SetAttributeAction::InternalProcessAction(ProcessActionCallback callback) {
void SetAttributeAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
index 57b3f9d1920..7bb43d21c2e 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/set_attribute_action.h
@@ -27,7 +27,7 @@ class SetAttributeAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found);
+ const ClientStatus& element_status);
void OnSetAttribute(ProcessActionCallback callback,
const ClientStatus& status);
diff --git a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.cc b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
index 80552b759b9..c3615cf4745 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
@@ -100,6 +100,24 @@ void SetFormFieldValueAction::InternalProcessAction(
// Currently no check required.
field_inputs_.emplace_back(/* value = */ keypress.text());
break;
+ case SetFormFieldValueProto_KeyPress::kClientMemoryKey:
+ if (keypress.client_memory_key().empty()) {
+ DVLOG(1) << "SetFormFieldValueAction: empty |client_memory_key|";
+ EndAction(ClientStatus(INVALID_ACTION));
+ return;
+ }
+ if (!delegate_->GetClientMemory()->has_additional_value(
+ keypress.client_memory_key())) {
+ DVLOG(1) << "SetFormFieldValueAction: requested key '"
+ << keypress.client_memory_key()
+ << "' not available in client memory";
+ EndAction(ClientStatus(PRECONDITION_FAILED));
+ return;
+ }
+ field_inputs_.emplace_back(
+ /* value = */ *delegate_->GetClientMemory()->additional_value(
+ keypress.client_memory_key()));
+ break;
default:
DVLOG(1) << "Unrecognized field for SetFormFieldValueProto_KeyPress";
EndAction(ClientStatus(INVALID_ACTION));
@@ -112,9 +130,10 @@ void SetFormFieldValueAction::InternalProcessAction(
weak_ptr_factory_.GetWeakPtr()));
}
-void SetFormFieldValueAction::OnWaitForElement(bool element_found) {
- if (!element_found) {
- EndAction(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+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.
@@ -181,10 +200,10 @@ void SetFormFieldValueAction::OnSetFieldValueAndCheckFallback(
void SetFormFieldValueAction::OnGetFieldValue(
int field_index,
const std::string& requested_value,
- bool get_value_status,
+ const ClientStatus& element_status,
const std::string& actual_value) {
// Move to next value if |GetFieldValue| failed.
- if (!get_value_status) {
+ if (!element_status.ok()) {
OnSetFieldValue(field_index + 1, OkClientStatus());
return;
}
@@ -238,6 +257,8 @@ void SetFormFieldValueAction::OnGetPassword(int field_index,
}
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_));
}
diff --git a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.h b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.h
index b00c045999e..adfbf7b9fee 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
@@ -24,6 +25,9 @@ class SetFormFieldValueAction : public Action {
~SetFormFieldValueAction() override;
private:
+ FRIEND_TEST_ALL_PREFIXES(SetFormFieldValueActionTest,
+ PasswordIsClearedFromMemory);
+
// A field input as extracted from the proto, but already checked for
// validity.
struct FieldInput {
@@ -46,11 +50,11 @@ class SetFormFieldValueAction : public Action {
// Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override;
- void OnWaitForElement(bool element_found);
+ void OnWaitForElement(const ClientStatus& element_status);
void OnGetFieldValue(int field_index,
const std::string& requested_value,
- bool status,
+ const ClientStatus& element_status,
const std::string& actual_value);
void OnSetFieldValue(int next, const ClientStatus& status);
diff --git a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
index 4ca89445421..621f7254678 100644
--- a/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
@@ -24,7 +24,6 @@ const char kFakePassword[] = "example_password";
} // namespace
namespace autofill_assistant {
-namespace {
using ::base::test::RunOnceCallback;
using ::testing::_;
@@ -45,7 +44,7 @@ class SetFormFieldValueActionTest : public testing::Test {
ON_CALL(mock_action_delegate_, GetWebsiteLoginFetcher)
.WillByDefault(Return(&mock_website_login_fetcher_));
ON_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_action_delegate_, OnSetFieldValue(_, _, _, _, _))
.WillByDefault(RunOnceCallback<4>(OkClientStatus()));
@@ -119,7 +118,7 @@ TEST_F(SetFormFieldValueActionTest, Username) {
value->set_use_username(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, kFakeUsername));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), kFakeUsername));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, kFakeUsername, _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
@@ -135,7 +134,7 @@ TEST_F(SetFormFieldValueActionTest, Password) {
value->set_use_password(true);
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, kFakePassword));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), kFakePassword));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, kFakePassword, _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
@@ -181,7 +180,7 @@ TEST_F(SetFormFieldValueActionTest, Text) {
value->set_text("SomeText𠜎");
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, "SomeText𠜎"));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "SomeText𠜎"));
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(fake_selector_, "SomeText𠜎", _, _, _))
.WillOnce(RunOnceCallback<4>(OkClientStatus()));
@@ -192,6 +191,33 @@ TEST_F(SetFormFieldValueActionTest, Text) {
action.ProcessAction(callback_.Get());
}
+TEST_F(SetFormFieldValueActionTest, ClientMemoryKey) {
+ auto* value = set_form_field_proto_->add_value();
+ value->set_client_memory_key("key");
+ client_memory_.set_additional_value("key", "SomeText𠜎");
+ SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+ ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "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_F(SetFormFieldValueActionTest, ClientMemoryKeyFailsIfNotInClientMemory) {
+ auto* value = set_form_field_proto_->add_value();
+ value->set_client_memory_key("key");
+ SetFormFieldValueAction action(&mock_action_delegate_, proto_);
+
+ EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
+ PRECONDITION_FAILED))));
+ action.ProcessAction(callback_.Get());
+}
+
// Test that automatic fallback to simulate keystrokes works.
TEST_F(SetFormFieldValueActionTest, Fallback) {
auto* value = set_form_field_proto_->add_value();
@@ -199,7 +225,7 @@ TEST_F(SetFormFieldValueActionTest, Fallback) {
SetFormFieldValueAction action(&mock_action_delegate_, proto_);
ON_CALL(mock_action_delegate_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, ""));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), ""));
{
InSequence seq;
@@ -222,5 +248,14 @@ TEST_F(SetFormFieldValueActionTest, Fallback) {
action.ProcessAction(callback_.Get());
}
-} // namespace
-} // namespace autofill_assistant \ No newline at end of file
+TEST_F(SetFormFieldValueActionTest, PasswordIsClearedFromMemory) {
+ 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>(OkClientStatus(), kFakePassword));
+ action.ProcessAction(callback_.Get());
+ EXPECT_TRUE(action.field_inputs_.empty());
+}
+
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/show_progress_bar_action.cc b/chromium/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
index 5a0ec991479..45e05cfadd9 100644
--- a/chromium/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
@@ -4,12 +4,12 @@
#include "components/autofill_assistant/browser/actions/show_progress_bar_action.h"
-#include <algorithm>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
+#include "base/numerics/ranges.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
namespace autofill_assistant {
@@ -28,7 +28,7 @@ void ShowProgressBarAction::InternalProcessAction(
delegate_->SetStatusMessage(proto_.show_progress_bar().message());
}
int progress =
- std::min(100, std::max(0, proto_.show_progress_bar().progress()));
+ base::ClampToRange(proto_.show_progress_bar().progress(), 0, 100);
delegate_->SetProgress(progress);
if (proto_.show_progress_bar().has_hide()) {
delegate_->SetProgressVisible(!proto_.show_progress_bar().hide());
diff --git a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.cc b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.cc
index a339b62eb84..9d3c8a20d66 100644
--- a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.cc
@@ -36,9 +36,9 @@ void UploadDomAction::InternalProcessAction(ProcessActionCallback callback) {
void UploadDomAction::OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found) {
- if (!element_found) {
- UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ UpdateProcessedAction(element_status.proto_status());
std::move(callback).Run(std::move(processed_action_proto_));
return;
}
diff --git a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
index 1d757f74f62..1d0cc50eed5 100644
--- a/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/upload_dom_action.h
@@ -24,7 +24,7 @@ class UploadDomAction : public Action {
void OnWaitForElement(ProcessActionCallback callback,
const Selector& selector,
- bool element_found);
+ const ClientStatus& element_status);
void OnGetOuterHtml(ProcessActionCallback callback,
const ClientStatus& status,
const std::string& outer_html);
diff --git a/chromium/components/autofill_assistant/browser/actions/use_address_action.cc b/chromium/components/autofill_assistant/browser/actions/use_address_action.cc
new file mode 100644
index 00000000000..61632587c87
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/use_address_action.cc
@@ -0,0 +1,169 @@
+// 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/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.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/required_fields_fallback_handler.h"
+#include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/client_status.h"
+
+namespace autofill_assistant {
+using RequiredField = RequiredFieldsFallbackHandler::RequiredField;
+using FallbackData = RequiredFieldsFallbackHandler::FallbackData;
+
+UseAddressAction::UseAddressAction(ActionDelegate* delegate,
+ const ActionProto& proto)
+ : Action(delegate, proto) {
+ DCHECK(proto.has_use_address());
+ prompt_ = proto.use_address().prompt();
+ name_ = proto.use_address().name();
+ std::vector<RequiredField> required_fields;
+ for (const auto& required_field_proto :
+ proto_.use_address().required_fields()) {
+ required_fields.emplace_back();
+ RequiredField& required_field = required_fields.back();
+ required_field.address_field = required_field_proto.address_field();
+ required_field.selector = Selector(required_field_proto.element());
+ required_field.simulate_key_presses =
+ required_field_proto.simulate_key_presses();
+ required_field.delay_in_millisecond =
+ required_field_proto.delay_in_millisecond();
+ required_field.forced = required_field_proto.forced();
+ }
+
+ required_fields_fallback_handler_ =
+ std::make_unique<RequiredFieldsFallbackHandler>(
+ required_fields,
+ base::BindRepeating(&UseAddressAction::GetFallbackValue,
+ base::Unretained(this)),
+ base::BindOnce(&UseAddressAction::EndAction, base::Unretained(this)),
+ delegate);
+ selector_ = Selector(proto.use_address().form_field_element());
+ selector_.MustBeVisible();
+ DCHECK(!selector_.empty());
+}
+
+UseAddressAction::~UseAddressAction() = default;
+
+void UseAddressAction::InternalProcessAction(
+ ProcessActionCallback action_callback) {
+ process_action_callback_ = std::move(action_callback);
+
+ // Ensure data already selected in a previous action.
+ auto* client_memory = delegate_->GetClientMemory();
+ if (!client_memory->has_selected_address(name_)) {
+ auto* error_info = processed_action_proto_->mutable_status_details()
+ ->mutable_autofill_error_info();
+ error_info->set_address_key_requested(name_);
+ error_info->set_client_memory_address_key_names(
+ client_memory->GetAllAddressKeyNames());
+ error_info->set_address_pointee_was_null(
+ !client_memory->has_selected_address(name_) ||
+ !client_memory->selected_address(name_));
+ EndAction(ClientStatus(PRECONDITION_FAILED));
+ return;
+ }
+
+ FillFormWithData();
+}
+
+void UseAddressAction::EndAction(
+ const ClientStatus& final_status,
+ const base::Optional<ClientStatus>& optional_details_status) {
+ UpdateProcessedAction(final_status);
+ if (optional_details_status.has_value() && !optional_details_status->ok()) {
+ processed_action_proto_->mutable_status_details()->MergeFrom(
+ optional_details_status->details());
+ }
+ std::move(process_action_callback_).Run(std::move(processed_action_proto_));
+}
+
+void UseAddressAction::FillFormWithData() {
+ delegate_->ShortWaitForElement(
+ selector_, base::BindOnce(&UseAddressAction::OnWaitForElement,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ EndAction(ClientStatus(element_status.proto_status()));
+ return;
+ }
+
+ DCHECK(!selector_.empty());
+ DVLOG(3) << "Retrieving address from client memory under '" << name_ << "'.";
+ const autofill::AutofillProfile* profile =
+ delegate_->GetClientMemory()->selected_address(name_);
+ DCHECK(profile);
+ auto fallback_data = std::make_unique<FallbackData>();
+ fallback_data->profile = profile;
+ delegate_->FillAddressForm(
+ profile, selector_,
+ base::BindOnce(&UseAddressAction::OnFormFilled,
+ weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
+}
+
+void UseAddressAction::OnFormFilled(std::unique_ptr<FallbackData> fallback_data,
+ const ClientStatus& status) {
+ required_fields_fallback_handler_->CheckAndFallbackRequiredFields(
+ status, std::move(fallback_data));
+}
+
+std::string UseAddressAction::GetFallbackValue(
+ const RequiredField& required_field,
+ const FallbackData& fallback_data) {
+ return base::UTF16ToUTF8(
+ GetFieldValue(fallback_data.profile, required_field.address_field));
+}
+
+base::string16 UseAddressAction::GetFieldValue(
+ const autofill::AutofillProfile* profile,
+ const UseAddressProto::RequiredField::AddressField& address_field) {
+ // TODO(crbug.com/806868): Get the actual application locale.
+ std::string app_locale = "en-US";
+ switch (address_field) {
+ case UseAddressProto::RequiredField::FIRST_NAME:
+ return profile->GetInfo(autofill::NAME_FIRST, app_locale);
+ case UseAddressProto::RequiredField::LAST_NAME:
+ return profile->GetInfo(autofill::NAME_LAST, app_locale);
+ case UseAddressProto::RequiredField::FULL_NAME:
+ return profile->GetInfo(autofill::NAME_FULL, app_locale);
+ case UseAddressProto::RequiredField::PHONE_NUMBER:
+ return profile->GetInfo(autofill::PHONE_HOME_WHOLE_NUMBER, app_locale);
+ case UseAddressProto::RequiredField::EMAIL:
+ return profile->GetInfo(autofill::EMAIL_ADDRESS, app_locale);
+ case UseAddressProto::RequiredField::ORGANIZATION:
+ return profile->GetInfo(autofill::COMPANY_NAME, app_locale);
+ case UseAddressProto::RequiredField::COUNTRY_CODE:
+ return profile->GetInfo(autofill::ADDRESS_HOME_COUNTRY, app_locale);
+ case UseAddressProto::RequiredField::REGION:
+ return profile->GetInfo(autofill::ADDRESS_HOME_STATE, app_locale);
+ case UseAddressProto::RequiredField::STREET_ADDRESS:
+ return profile->GetInfo(autofill::ADDRESS_HOME_STREET_ADDRESS,
+ app_locale);
+ case UseAddressProto::RequiredField::LOCALITY:
+ return profile->GetInfo(autofill::ADDRESS_HOME_CITY, app_locale);
+ case UseAddressProto::RequiredField::DEPENDANT_LOCALITY:
+ return profile->GetInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
+ app_locale);
+ case UseAddressProto::RequiredField::POSTAL_CODE:
+ return profile->GetInfo(autofill::ADDRESS_HOME_ZIP, app_locale);
+ case UseAddressProto::RequiredField::UNDEFINED:
+ NOTREACHED();
+ return base::string16();
+ }
+}
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/use_address_action.h b/chromium/components/autofill_assistant/browser/actions/use_address_action.h
new file mode 100644
index 00000000000..3977f5d52fa
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/use_address_action.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_USE_ADDRESS_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_USE_ADDRESS_ACTION_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
+
+namespace autofill {
+class AutofillProfile;
+} // namespace autofill
+
+namespace autofill_assistant {
+class ClientStatus;
+
+// An action to autofill a form using a local address.
+class UseAddressAction : public Action {
+ public:
+ explicit UseAddressAction(ActionDelegate* delegate, const ActionProto& proto);
+ ~UseAddressAction() override;
+
+ private:
+ // Overrides Action:
+ void InternalProcessAction(ProcessActionCallback callback) override;
+
+ void EndAction(const ClientStatus& final_status,
+ const base::Optional<ClientStatus>& optional_details_status =
+ base::nullopt);
+
+ // Fill the form using data in client memory. Return whether filling succeeded
+ // or not through OnFormFilled.
+ void FillFormWithData();
+ void OnWaitForElement(const ClientStatus& element_status);
+
+ // Called when the address has been filled.
+ void OnFormFilled(std::unique_ptr<RequiredFieldsFallbackHandler::FallbackData>
+ fallback_data,
+ const ClientStatus& status);
+
+ // Gets the fallback value.
+ std::string GetFallbackValue(
+ const RequiredFieldsFallbackHandler::RequiredField& required_field,
+ const RequiredFieldsFallbackHandler::FallbackData& fallback_data);
+
+ // Get the value of |address_field| associated to profile |profile|. Return
+ // empty string if there is no data available.
+ base::string16 GetFieldValue(
+ const autofill::AutofillProfile* profile,
+ const UseAddressProto::RequiredField::AddressField& address_field);
+
+ // Usage of the autofilled address.
+ std::string name_;
+ std::string prompt_;
+ Selector selector_;
+
+ std::unique_ptr<RequiredFieldsFallbackHandler>
+ required_fields_fallback_handler_;
+
+ ProcessActionCallback process_action_callback_;
+ base::WeakPtrFactory<UseAddressAction> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(UseAddressAction);
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_AUTOFILL_ACTION_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/use_address_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/use_address_action_unittest.cc
new file mode 100644
index 00000000000..00de8237c26
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/use_address_action_unittest.cc
@@ -0,0 +1,345 @@
+// 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 "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/mock_personal_data_manager.h"
+#include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Expectation;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SaveArgPointee;
+
+class UseAddressActionTest : public testing::Test {
+ public:
+ void SetUp() override {
+ // Build two identical autofill profiles. One for the memory, one for the
+ // mock.
+ auto autofill_profile = std::make_unique<autofill::AutofillProfile>(
+ base::GenerateGUID(), autofill::test::kEmptyOrigin);
+ autofill::test::SetProfileInfo(autofill_profile.get(), kFirstName, "",
+ kLastName, kEmail, "", "", "", "", "", "",
+ "", "");
+ autofill::test::SetProfileInfo(&autofill_profile_, kFirstName, "",
+ kLastName, kEmail, "", "", "", "", "", "",
+ "", "");
+ client_memory_.set_selected_address(kAddressName,
+ std::move(autofill_profile));
+
+ ON_CALL(mock_personal_data_manager_, GetProfileByGUID)
+ .WillByDefault(Return(&autofill_profile_));
+ ON_CALL(mock_action_delegate_, GetClientMemory)
+ .WillByDefault(Return(&client_memory_));
+ ON_CALL(mock_action_delegate_, GetPersonalDataManager)
+ .WillByDefault(Return(&mock_personal_data_manager_));
+ ON_CALL(mock_action_delegate_, RunElementChecks)
+ .WillByDefault(Invoke([this](BatchElementChecker* checker) {
+ checker->Run(&mock_web_controller_);
+ }));
+ ON_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
+ }
+
+ protected:
+ const char* const kAddressName = "billing";
+ const char* const kFakeSelector = "#selector";
+ const char* const kSelectionPrompt = "prompt";
+ const char* const kFirstName = "FirstName";
+ const char* const kLastName = "LastName";
+ const char* const kEmail = "foobar@gmail.com";
+
+ ActionProto CreateUseAddressAction() {
+ ActionProto action;
+ UseAddressProto* use_address = action.mutable_use_address();
+ use_address->set_name(kAddressName);
+ use_address->mutable_form_field_element()->add_selectors(kFakeSelector);
+ return action;
+ }
+
+ UseAddressProto::RequiredField* AddRequiredField(
+ ActionProto* action,
+ UseAddressProto::RequiredField::AddressField type,
+ std::string selector) {
+ auto* required_field = action->mutable_use_address()->add_required_fields();
+ required_field->set_address_field(type);
+ required_field->mutable_element()->add_selectors(selector);
+ return required_field;
+ }
+
+ ProcessedActionStatusProto ProcessAction(const ActionProto& action_proto) {
+ UseAddressAction action(&mock_action_delegate_, action_proto);
+ ProcessedActionProto capture;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&capture));
+ action.ProcessAction(callback_.Get());
+ return capture.status();
+ }
+
+ base::MockCallback<Action::ProcessActionCallback> callback_;
+ MockPersonalDataManager mock_personal_data_manager_;
+ MockActionDelegate mock_action_delegate_;
+ MockWebController mock_web_controller_;
+ ClientMemory client_memory_;
+
+ autofill::AutofillProfile autofill_profile_;
+};
+
+#if !defined(OS_ANDROID)
+#define MAYBE_FillManually FillManually
+#else
+#define MAYBE_FillManually DISABLED_FillManually
+#endif
+TEST_F(UseAddressActionTest, MAYBE_FillManually) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
+
+ EXPECT_EQ(ProcessedActionStatusProto::MANUAL_FALLBACK,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, NoSelectedAddress) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
+
+ client_memory_.set_selected_address(kAddressName, nullptr);
+
+ EXPECT_EQ(ProcessedActionStatusProto::PRECONDITION_FAILED,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, PreconditionFailedPopulatesUnexpectedErrorInfo) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
+ client_memory_.set_selected_address(kAddressName, nullptr);
+ client_memory_.set_selected_address("one_more", nullptr);
+
+ UseAddressAction action(&mock_action_delegate_, action_proto);
+
+ ProcessedActionProto processed_action;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_EQ(ProcessedActionStatusProto::PRECONDITION_FAILED,
+ processed_action.status());
+ const auto& error_info =
+ processed_action.status_details().autofill_error_info();
+ EXPECT_EQ(base::JoinString({kAddressName, "one_more"}, ","),
+ error_info.client_memory_address_key_names());
+ EXPECT_EQ(kAddressName, error_info.address_key_requested());
+ EXPECT_TRUE(error_info.address_pointee_was_null());
+}
+
+TEST_F(UseAddressActionTest, ShortWaitForElementVisible) {
+ EXPECT_CALL(
+ mock_action_delegate_,
+ OnShortWaitForElement(Selector({kFakeSelector}).MustBeVisible(), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
+
+ ActionProto action_proto = CreateUseAddressAction();
+ // Autofill succeeds.
+ EXPECT_CALL(mock_action_delegate_, OnFillAddressForm(NotNull(), _, _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+
+ // Validation succeeds.
+ ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, ValidationSucceeds) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
+ "#first_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
+ "#last_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
+ "#email");
+
+ // Autofill succeeds.
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillAddressForm(
+ NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+
+ // Validation succeeds.
+ ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, FallbackFails) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
+ "#first_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
+ "#last_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
+ "#email");
+
+ // Autofill succeeds.
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillAddressForm(
+ NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+
+ // Validation fails when getting FIRST_NAME.
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#email"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#first_name"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#last_name"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ // Fallback fails.
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Eq(Selector({"#first_name"})), kFirstName, _))
+ .WillOnce(RunOnceCallback<2>(ClientStatus(OTHER_ACTION_STATUS)));
+
+ EXPECT_EQ(ProcessedActionStatusProto::MANUAL_FALLBACK,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, FallbackSucceeds) {
+ InSequence seq;
+
+ ActionProto action_proto = CreateUseAddressAction();
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
+ "#first_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
+ "#last_name");
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
+ "#email");
+
+ // Autofill succeeds.
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillAddressForm(
+ NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+
+ {
+ InSequence seq;
+
+ // Validation fails when getting FIRST_NAME.
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#email"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#first_name"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Eq(Selector({"#last_name"})), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ // Fallback succeeds.
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Eq(Selector({"#first_name"})), kFirstName, _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+
+ // Second validation succeeds.
+ EXPECT_CALL(mock_web_controller_, OnGetFieldValue(_, _))
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ }
+ EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
+ ProcessAction(action_proto));
+}
+
+TEST_F(UseAddressActionTest, AutofillFailureWithoutRequiredFieldsIsFatal) {
+ ActionProto action_proto = CreateUseAddressAction();
+
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillAddressForm(
+ NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
+ .WillOnce(RunOnceCallback<2>(ClientStatus(OTHER_ACTION_STATUS)));
+
+ ProcessedActionProto processed_action;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
+
+ UseAddressAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_EQ(processed_action.status(),
+ ProcessedActionStatusProto::OTHER_ACTION_STATUS);
+ EXPECT_EQ(processed_action.has_status_details(), false);
+}
+
+TEST_F(UseAddressActionTest,
+ AutofillFailureWithRequiredFieldsLaunchesFallback) {
+ ActionProto action_proto = CreateUseAddressAction();
+ AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
+ "#first_name");
+
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillAddressForm(
+ NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
+ .WillOnce(RunOnceCallback<2>(
+ FillAutofillErrorStatus(ClientStatus(OTHER_ACTION_STATUS))));
+
+ // First validation fails.
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#first_name"}), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ // Fill first name.
+ Expectation set_first_name =
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Selector({"#first_name"}), kFirstName, _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+ // Second validation succeeds.
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#first_name"}), _))
+ .After(set_first_name)
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ ProcessedActionProto processed_action;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
+
+ UseAddressAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_EQ(processed_action.status(),
+ ProcessedActionStatusProto::ACTION_APPLIED);
+ EXPECT_EQ(processed_action.status_details()
+ .autofill_error_info()
+ .autofill_error_status(),
+ OTHER_ACTION_STATUS);
+}
+
+} // namespace
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.cc b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.cc
new file mode 100644
index 00000000000..e204701dd5f
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.cc
@@ -0,0 +1,179 @@
+// 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_credit_card_action.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
+#include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/client_status.h"
+
+namespace autofill_assistant {
+using RequiredField = RequiredFieldsFallbackHandler::RequiredField;
+using FallbackData = RequiredFieldsFallbackHandler::FallbackData;
+
+UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate,
+ const ActionProto& proto)
+ : Action(delegate, proto) {
+ DCHECK(proto.has_use_card());
+ prompt_ = proto.use_card().prompt();
+ std::vector<RequiredField> required_fields;
+ for (const auto& required_field_proto : proto_.use_card().required_fields()) {
+ required_fields.emplace_back();
+ RequiredField& required_field = required_fields.back();
+ required_field.card_field = required_field_proto.card_field();
+ required_field.selector = Selector(required_field_proto.element());
+ required_field.simulate_key_presses =
+ required_field_proto.simulate_key_presses();
+ required_field.delay_in_millisecond =
+ required_field_proto.delay_in_millisecond();
+ required_field.forced = required_field_proto.forced();
+ }
+
+ required_fields_fallback_handler_ =
+ std::make_unique<RequiredFieldsFallbackHandler>(
+ required_fields,
+ base::BindRepeating(&UseCreditCardAction::GetFallbackValue,
+ base::Unretained(this)),
+ base::BindOnce(&UseCreditCardAction::EndAction,
+ base::Unretained(this)),
+ delegate);
+ selector_ = Selector(proto.use_card().form_field_element());
+ selector_.MustBeVisible();
+ DCHECK(!selector_.empty());
+}
+
+UseCreditCardAction::~UseCreditCardAction() = default;
+
+void UseCreditCardAction::InternalProcessAction(
+ ProcessActionCallback action_callback) {
+ process_action_callback_ = std::move(action_callback);
+
+ // Ensure data already selected in a previous action.
+ auto* client_memory = delegate_->GetClientMemory();
+ if (!client_memory->has_selected_card()) {
+ EndAction(ClientStatus(PRECONDITION_FAILED));
+ return;
+ }
+
+ FillFormWithData();
+}
+
+void UseCreditCardAction::EndAction(
+ const ClientStatus& final_status,
+ const base::Optional<ClientStatus>& optional_details_status) {
+ UpdateProcessedAction(final_status);
+ if (optional_details_status.has_value() && !optional_details_status->ok()) {
+ processed_action_proto_->mutable_status_details()->MergeFrom(
+ optional_details_status->details());
+ }
+ std::move(process_action_callback_).Run(std::move(processed_action_proto_));
+}
+
+void UseCreditCardAction::FillFormWithData() {
+ delegate_->ShortWaitForElement(
+ selector_, base::BindOnce(&UseCreditCardAction::OnWaitForElement,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ EndAction(ClientStatus(element_status.proto_status()));
+ return;
+ }
+
+ delegate_->GetFullCard(base::BindOnce(&UseCreditCardAction::OnGetFullCard,
+ weak_ptr_factory_.GetWeakPtr()));
+ return;
+}
+
+void UseCreditCardAction::OnGetFullCard(
+ std::unique_ptr<autofill::CreditCard> card,
+ const base::string16& cvc) {
+ if (!card) {
+ EndAction(ClientStatus(GET_FULL_CARD_FAILED));
+ return;
+ }
+
+ auto fallback_data = std::make_unique<FallbackData>();
+ fallback_data->cvc = base::UTF16ToUTF8(cvc);
+ fallback_data->expiration_month = card->expiration_month();
+ fallback_data->expiration_year = card->expiration_year();
+ fallback_data->card_holder_name =
+ base::UTF16ToUTF8(card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
+ fallback_data->card_number =
+ base::UTF16ToUTF8(card->GetRawInfo(autofill::CREDIT_CARD_NUMBER));
+
+ delegate_->FillCardForm(
+ std::move(card), cvc, selector_,
+ base::BindOnce(&UseCreditCardAction::OnFormFilled,
+ weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
+}
+
+void UseCreditCardAction::OnFormFilled(
+ std::unique_ptr<FallbackData> fallback_data,
+ const ClientStatus& status) {
+ required_fields_fallback_handler_->CheckAndFallbackRequiredFields(
+ status, std::move(fallback_data));
+}
+
+std::string UseCreditCardAction::GetFallbackValue(
+ const RequiredField& required_field,
+ const FallbackData& fallback_data) {
+ auto field = required_field.card_field;
+ switch (field) {
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE:
+ return fallback_data.cvc;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_MONTH:
+ if (fallback_data.expiration_month > 0)
+ return base::StringPrintf("%02d", fallback_data.expiration_month);
+ break;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ if (fallback_data.expiration_year > 0)
+ return base::StringPrintf("%02d", fallback_data.expiration_year % 100);
+ break;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ if (fallback_data.expiration_year > 0)
+ return base::NumberToString(fallback_data.expiration_year);
+ break;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_CARD_HOLDER_NAME:
+ return fallback_data.card_holder_name;
+ break;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_NUMBER:
+ return fallback_data.card_number;
+ break;
+
+ case UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_MM_YY:
+ if (fallback_data.expiration_month > 0 &&
+ fallback_data.expiration_year > 0)
+ return base::StrCat(
+ {base::StringPrintf("%02d", fallback_data.expiration_month), "/",
+ base::StringPrintf("%02d", fallback_data.expiration_year % 100)});
+ break;
+
+ case UseCreditCardProto::RequiredField::UNDEFINED:
+ NOTREACHED();
+ return "";
+ }
+ return "";
+}
+} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.h b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.h
new file mode 100644
index 00000000000..376ca61dd08
--- /dev/null
+++ b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_USE_CREDIT_CARD_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_USE_CREDIT_CARD_ACTION_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
+
+namespace autofill {
+class CreditCard;
+} // namespace autofill
+
+namespace autofill_assistant {
+class ClientStatus;
+
+// An action to autofill a form using a credit card.
+class UseCreditCardAction : public Action {
+ public:
+ explicit UseCreditCardAction(ActionDelegate* delegate,
+ const ActionProto& proto);
+ ~UseCreditCardAction() override;
+
+ private:
+ // Overrides Action:
+ void InternalProcessAction(ProcessActionCallback callback) override;
+
+ void EndAction(const ClientStatus& final_status,
+ const base::Optional<ClientStatus>& optional_details_status =
+ base::nullopt);
+
+ // Fill the form using data in client memory. Return whether filling succeeded
+ // or not through OnFormFilled.
+ void FillFormWithData();
+ void OnWaitForElement(const ClientStatus& element_status);
+
+ // Called after getting full credit card with its cvc.
+ void OnGetFullCard(std::unique_ptr<autofill::CreditCard> card,
+ const base::string16& cvc);
+
+ // Called when the form credit card has been filled.
+ void OnFormFilled(std::unique_ptr<RequiredFieldsFallbackHandler::FallbackData>
+ fallback_data,
+ const ClientStatus& status);
+
+ // Gets the fallback value.
+ std::string GetFallbackValue(
+ const RequiredFieldsFallbackHandler::RequiredField& required_field,
+ const RequiredFieldsFallbackHandler::FallbackData& fallback_data);
+
+ std::string prompt_;
+ Selector selector_;
+
+ std::unique_ptr<RequiredFieldsFallbackHandler>
+ required_fields_fallback_handler_;
+
+ ProcessActionCallback process_action_callback_;
+ base::WeakPtrFactory<UseCreditCardAction> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(UseCreditCardAction);
+};
+
+} // namespace autofill_assistant
+#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_AUTOFILL_ACTION_H_
diff --git a/chromium/components/autofill_assistant/browser/actions/autofill_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc
index e2ad48920ba..e6a24867a7d 100644
--- a/chromium/components/autofill_assistant/browser/actions/autofill_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc
@@ -2,7 +2,7 @@
// 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/autofill_action.h"
+#include "components/autofill_assistant/browser/actions/use_credit_card_action.h"
#include <utility>
@@ -15,6 +15,7 @@
#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
#include "components/autofill_assistant/browser/mock_personal_data_manager.h"
#include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "components/autofill_assistant/browser/web/web_controller_util.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
@@ -30,7 +31,7 @@ using ::testing::NotNull;
using ::testing::Return;
using ::testing::SaveArgPointee;
-class AutofillActionTest : public testing::Test {
+class UseCreditCardActionTest : public testing::Test {
public:
void SetUp() override {
// Build two identical autofill profiles. One for the memory, one for the
@@ -57,7 +58,7 @@ class AutofillActionTest : public testing::Test {
checker->Run(&mock_web_controller_);
}));
ON_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
}
protected:
@@ -68,14 +69,6 @@ class AutofillActionTest : public testing::Test {
const char* const kLastName = "LastName";
const char* const kEmail = "foobar@gmail.com";
- ActionProto CreateUseAddressAction() {
- ActionProto action;
- UseAddressProto* use_address = action.mutable_use_address();
- use_address->set_name(kAddressName);
- use_address->mutable_form_field_element()->add_selectors(kFakeSelector);
- return action;
- }
-
ActionProto CreateUseCreditCardAction() {
ActionProto action;
action.mutable_use_card()->mutable_form_field_element()->add_selectors(
@@ -83,16 +76,6 @@ class AutofillActionTest : public testing::Test {
return action;
}
- UseAddressProto::RequiredField* AddRequiredField(
- ActionProto* action,
- UseAddressProto::RequiredField::AddressField type,
- std::string selector) {
- auto* required_field = action->mutable_use_address()->add_required_fields();
- required_field->set_address_field(type);
- required_field->mutable_element()->add_selectors(selector);
- return required_field;
- }
-
UseCreditCardProto::RequiredField* AddRequiredField(
ActionProto* action,
UseCreditCardProto::RequiredField::CardField type,
@@ -111,7 +94,7 @@ class AutofillActionTest : public testing::Test {
}
ProcessedActionStatusProto ProcessAction(const ActionProto& action_proto) {
- AutofillAction action(&mock_action_delegate_, action_proto);
+ UseCreditCardAction action(&mock_action_delegate_, action_proto);
ProcessedActionProto capture;
EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&capture));
action.ProcessAction(callback_.Get());
@@ -127,188 +110,13 @@ class AutofillActionTest : public testing::Test {
autofill::AutofillProfile autofill_profile_;
};
-#if !defined(OS_ANDROID)
-#define MAYBE_FillManually FillManually
-#else
-#define MAYBE_FillManually DISABLED_FillManually
-#endif
-TEST_F(AutofillActionTest, MAYBE_FillManually) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
-
- EXPECT_EQ(ProcessedActionStatusProto::MANUAL_FALLBACK,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, NoSelectedAddress) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
-
- client_memory_.set_selected_address(kAddressName, nullptr);
-
- EXPECT_EQ(ProcessedActionStatusProto::PRECONDITION_FAILED,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, PreconditionFailedPopulatesUnexpectedErrorInfo) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- action_proto.mutable_use_address()->set_prompt(kSelectionPrompt);
- client_memory_.set_selected_address(kAddressName, nullptr);
- client_memory_.set_selected_address("one_more", nullptr);
-
- AutofillAction action(&mock_action_delegate_, action_proto);
-
- ProcessedActionProto processed_action;
- EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
- action.ProcessAction(callback_.Get());
-
- EXPECT_EQ(ProcessedActionStatusProto::PRECONDITION_FAILED,
- processed_action.status());
- const auto& error_info =
- processed_action.status_details().autofill_error_info();
- EXPECT_EQ(base::JoinString({kAddressName, "one_more"}, ","),
- error_info.client_memory_address_key_names());
- EXPECT_EQ(kAddressName, error_info.address_key_requested());
-}
-
-TEST_F(AutofillActionTest, ShortWaitForElementVisible) {
- EXPECT_CALL(
- mock_action_delegate_,
- OnShortWaitForElement(Selector({kFakeSelector}).MustBeVisible(), _))
- .WillOnce(RunOnceCallback<1>(true));
-
- ActionProto action_proto = CreateUseAddressAction();
- // Autofill succeeds.
- EXPECT_CALL(mock_action_delegate_, OnFillAddressForm(NotNull(), _, _))
- .WillOnce(RunOnceCallback<2>(OkClientStatus()));
-
- // Validation succeeds.
- ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, "not empty"));
-
- EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, ValidationSucceeds) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
- "#first_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
- "#last_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
- "#email");
-
- // Autofill succeeds.
- EXPECT_CALL(mock_action_delegate_,
- OnFillAddressForm(
- NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
- .WillOnce(RunOnceCallback<2>(OkClientStatus()));
-
- // Validation succeeds.
- ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, "not empty"));
-
- EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, FallbackFails) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
- "#first_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
- "#last_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
- "#email");
-
- // Autofill succeeds.
- EXPECT_CALL(mock_action_delegate_,
- OnFillAddressForm(
- NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
- .WillOnce(RunOnceCallback<2>(OkClientStatus()));
-
- // Validation fails when getting FIRST_NAME.
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#email"})), _))
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#first_name"})), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#last_name"})), _))
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
-
- // Fallback fails.
- EXPECT_CALL(mock_action_delegate_,
- OnSetFieldValue(Eq(Selector({"#first_name"})), kFirstName, _))
- .WillOnce(RunOnceCallback<2>(ClientStatus(OTHER_ACTION_STATUS)));
-
- EXPECT_EQ(ProcessedActionStatusProto::MANUAL_FALLBACK,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, FallbackSucceeds) {
- InSequence seq;
-
- ActionProto action_proto = CreateUseAddressAction();
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::FIRST_NAME,
- "#first_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::LAST_NAME,
- "#last_name");
- AddRequiredField(&action_proto, UseAddressProto::RequiredField::EMAIL,
- "#email");
-
- // Autofill succeeds.
- EXPECT_CALL(mock_action_delegate_,
- OnFillAddressForm(
- NotNull(), Eq(Selector({kFakeSelector}).MustBeVisible()), _))
- .WillOnce(RunOnceCallback<2>(OkClientStatus()));
-
- {
- InSequence seq;
-
- // Validation fails when getting FIRST_NAME.
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#email"})), _))
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#first_name"})), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
- EXPECT_CALL(mock_web_controller_,
- OnGetFieldValue(Eq(Selector({"#last_name"})), _))
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
-
- // Fallback succeeds.
- EXPECT_CALL(mock_action_delegate_,
- OnSetFieldValue(Eq(Selector({"#first_name"})), kFirstName, _))
- .WillOnce(RunOnceCallback<2>(OkClientStatus()));
-
- // Second validation succeeds.
- EXPECT_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillRepeatedly(RunOnceCallback<1>(true, "not empty"));
- }
- EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED,
- ProcessAction(action_proto));
-}
-
-TEST_F(AutofillActionTest, FillCreditCardNoCardSelected) {
+TEST_F(UseCreditCardActionTest, FillCreditCardNoCardSelected) {
ActionProto action = CreateUseCreditCardAction();
EXPECT_EQ(ProcessedActionStatusProto::PRECONDITION_FAILED,
ProcessAction(action));
}
-TEST_F(AutofillActionTest, FillCreditCard) {
+TEST_F(UseCreditCardActionTest, FillCreditCard) {
ActionProto action = CreateUseCreditCardAction();
autofill::CreditCard credit_card;
@@ -324,10 +132,10 @@ TEST_F(AutofillActionTest, FillCreditCard) {
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
}
-TEST_F(AutofillActionTest, FillCreditCardRequiredFieldsFilled) {
+TEST_F(UseCreditCardActionTest, FillCreditCardRequiredFieldsFilled) {
// Validation succeeds.
ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, "not empty"));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
ActionProto action = CreateUseCreditCardAction();
AddRequiredField(
@@ -350,7 +158,7 @@ TEST_F(AutofillActionTest, FillCreditCardRequiredFieldsFilled) {
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
}
-TEST_F(AutofillActionTest, FillCreditCardWithFallback) {
+TEST_F(UseCreditCardActionTest, FillCreditCardWithFallback) {
ActionProto action = CreateUseCreditCardAction();
AddRequiredField(
&action, UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE,
@@ -364,16 +172,34 @@ TEST_F(AutofillActionTest, FillCreditCardWithFallback) {
AddRequiredField(
&action, UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_4_DIGIT_YEAR,
"#expyear4");
+ AddRequiredField(
+ &action, UseCreditCardProto::RequiredField::CREDIT_CARD_CARD_HOLDER_NAME,
+ "#card_name");
+ AddRequiredField(&action,
+ UseCreditCardProto::RequiredField::CREDIT_CARD_NUMBER,
+ "#card_number");
+ AddRequiredField(&action,
+ UseCreditCardProto::RequiredField::CREDIT_CARD_EXP_MM_YY,
+ "#exp_month_year2");
// First validation fails.
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expmonth"}), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expyear2"}), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expyear4"}), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#card_name"}), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#card_number"}), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#exp_month_year2"}), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
// Expect fields to be filled
Expectation set_cvc =
@@ -392,24 +218,53 @@ TEST_F(AutofillActionTest, FillCreditCardWithFallback) {
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(Selector({"#expyear4"}), "2024", _))
.WillOnce(RunOnceCallback<2>(OkClientStatus()));
+ Expectation set_cardholder_name =
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Selector({"#card_name"}), "Jon Doe", _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+ Expectation set_card_number =
+ EXPECT_CALL(
+ mock_action_delegate_,
+ OnSetFieldValue(Selector({"#card_number"}), "4111111111111111", _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+ Expectation set_exp_month_year2 =
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Selector({"#exp_month_year2"}), "09/24", _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
// After fallback, second validation succeeds.
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
.After(set_cvc)
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expmonth"}), _))
.After(set_expmonth)
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expyear2"}), _))
.After(set_expyear2)
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#expyear4"}), _))
.After(set_expyear4)
- .WillOnce(RunOnceCallback<1>(true, "not empty"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#card_name"}), _))
+ .After(set_expyear4)
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#card_number"}), _))
+ .After(set_expyear4)
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+ EXPECT_CALL(mock_web_controller_,
+ OnGetFieldValue(Selector({"#exp_month_year2"}), _))
+ .After(set_expyear4)
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
autofill::CreditCard credit_card;
credit_card.SetExpirationMonth(9);
credit_card.SetExpirationYear(2024);
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
+ base::UTF8ToUTF16("Jon Doe"));
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER,
+ base::UTF8ToUTF16("4111111111111111"));
client_memory_.set_selected_card(
std::make_unique<autofill::CreditCard>(credit_card));
EXPECT_CALL(mock_action_delegate_, OnGetFullCard(_))
@@ -422,7 +277,7 @@ TEST_F(AutofillActionTest, FillCreditCardWithFallback) {
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
}
-TEST_F(AutofillActionTest, ForcedFallback) {
+TEST_F(UseCreditCardActionTest, ForcedFallback) {
ActionProto action = CreateUseCreditCardAction();
auto* cvc_required = AddRequiredField(
&action, UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE,
@@ -433,7 +288,7 @@ TEST_F(AutofillActionTest, ForcedFallback) {
// No field is ever empty
ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(true, "not empty"));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
// But we still want the CVC filled, with simulated keypresses.
Expectation set_cvc =
@@ -454,5 +309,74 @@ TEST_F(AutofillActionTest, ForcedFallback) {
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
}
+TEST_F(UseCreditCardActionTest, AutofillFailureWithoutRequiredFieldsIsFatal) {
+ ActionProto action_proto = CreateUseCreditCardAction();
+
+ autofill::CreditCard credit_card;
+ client_memory_.set_selected_card(
+ std::make_unique<autofill::CreditCard>(credit_card));
+ EXPECT_CALL(mock_action_delegate_, OnGetFullCard(_))
+ .WillOnce(RunOnceCallback<0>(credit_card, base::UTF8ToUTF16("123")));
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillCardForm(_, base::UTF8ToUTF16("123"),
+ Selector({kFakeSelector}).MustBeVisible(), _))
+ .WillOnce(RunOnceCallback<3>(ClientStatus(OTHER_ACTION_STATUS)));
+
+ ProcessedActionProto processed_action;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
+
+ UseCreditCardAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_EQ(processed_action.status(),
+ ProcessedActionStatusProto::OTHER_ACTION_STATUS);
+ EXPECT_EQ(processed_action.has_status_details(), false);
+}
+
+TEST_F(UseCreditCardActionTest,
+ AutofillFailureWithRequiredFieldsLaunchesFallback) {
+ ActionProto action_proto = CreateUseCreditCardAction();
+ AddRequiredField(
+ &action_proto,
+ UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE, "#cvc");
+
+ autofill::CreditCard credit_card;
+ client_memory_.set_selected_card(
+ std::make_unique<autofill::CreditCard>(credit_card));
+ EXPECT_CALL(mock_action_delegate_, OnGetFullCard(_))
+ .WillOnce(RunOnceCallback<0>(credit_card, base::UTF8ToUTF16("123")));
+ EXPECT_CALL(mock_action_delegate_,
+ OnFillCardForm(_, base::UTF8ToUTF16("123"),
+ Selector({kFakeSelector}).MustBeVisible(), _))
+ .WillOnce(RunOnceCallback<3>(
+ FillAutofillErrorStatus(ClientStatus(OTHER_ACTION_STATUS))));
+
+ // First validation fails.
+ EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
+ // Fill CVC.
+ Expectation set_cvc =
+ EXPECT_CALL(mock_action_delegate_,
+ OnSetFieldValue(Selector({"#cvc"}), "123", _))
+ .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+ // Second validation succeeds.
+ EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
+ .After(set_cvc)
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
+
+ ProcessedActionProto processed_action;
+ EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
+
+ UseCreditCardAction action(&mock_action_delegate_, action_proto);
+ action.ProcessAction(callback_.Get());
+
+ EXPECT_EQ(processed_action.status(),
+ ProcessedActionStatusProto::ACTION_APPLIED);
+ EXPECT_EQ(processed_action.status_details()
+ .autofill_error_info()
+ .autofill_error_status(),
+ OTHER_ACTION_STATUS);
+}
+
} // namespace
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
index 384aa921954..976ad325ade 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.cc
@@ -24,7 +24,7 @@ void WaitForDocumentAction::InternalProcessAction(
Selector selector(proto_.wait_for_document().frame());
if (selector.empty()) {
// No element to wait for.
- OnShortWaitForElement(/* element_found= */ true);
+ OnShortWaitForElement(ClientStatus(ACTION_APPLIED));
return;
}
delegate_->ShortWaitForElement(
@@ -32,10 +32,10 @@ void WaitForDocumentAction::InternalProcessAction(
weak_ptr_factory_.GetWeakPtr()));
}
-void WaitForDocumentAction::OnShortWaitForElement(bool element_found) {
- if (!element_found) {
- SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED),
- DOCUMENT_UNKNOWN_READY_STATE);
+void WaitForDocumentAction::OnShortWaitForElement(
+ const ClientStatus& element_status) {
+ if (!element_status.ok()) {
+ SendResult(element_status, DOCUMENT_UNKNOWN_READY_STATE);
return;
}
delegate_->GetDocumentReadyState(
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
index 1667cbe6459..a8179934221 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action.h
@@ -22,7 +22,7 @@ class WaitForDocumentAction : public Action {
// Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override;
- void OnShortWaitForElement(bool element_found);
+ void OnShortWaitForElement(const ClientStatus& status);
void OnGetStartState(const ClientStatus& status,
DocumentReadyState start_state);
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action_unittest.cc
index db02dac65cf..7cf07a0faad 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_document_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_document_action_unittest.cc
@@ -185,7 +185,7 @@ TEST_F(WaitForDocumentActionTest, WaitForDocumentInteractiveTimesOut) {
TEST_F(WaitForDocumentActionTest, CheckDocumentInFrame) {
EXPECT_CALL(mock_action_delegate_,
OnShortWaitForElement(Selector({"#frame"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_action_delegate_,
OnGetDocumentReadyState(Selector({"#frame"}), _))
@@ -199,7 +199,8 @@ TEST_F(WaitForDocumentActionTest, CheckDocumentInFrame) {
TEST_F(WaitForDocumentActionTest, CheckFrameElementNotFound) {
EXPECT_CALL(mock_action_delegate_, OnShortWaitForElement(_, _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(
+ RunOnceCallback<1>(ClientStatus(ELEMENT_RESOLUTION_FAILED)));
proto_.set_timeout_ms(0);
proto_.mutable_frame()->add_selectors("#frame");
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
index 7fdc7791df5..339e003555d 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
@@ -35,14 +35,14 @@ void WaitForDomAction::InternalProcessAction(ProcessActionCallback callback) {
AddConditionsFromProto();
if (conditions_.empty()) {
DVLOG(2) << "WaitForDomAction: no selectors specified";
- OnCheckDone(std::move(callback), INVALID_ACTION);
+ OnCheckDone(std::move(callback), ClientStatus(INVALID_ACTION));
return;
}
for (size_t i = 0; i < conditions_.size(); i++) {
if (conditions_[i].selector.empty()) {
DVLOG(2) << "WaitForDomAction: selector for condition " << i
<< " is empty";
- OnCheckDone(std::move(callback), INVALID_SELECTOR);
+ OnCheckDone(std::move(callback), ClientStatus(INVALID_SELECTOR));
return;
}
}
@@ -112,8 +112,9 @@ void WaitForDomAction::AddCondition(SelectorPredicate predicate,
condition.server_payload = server_payload;
}
-void WaitForDomAction::CheckElements(BatchElementChecker* checker,
- base::OnceCallback<void(bool)> callback) {
+void WaitForDomAction::CheckElements(
+ BatchElementChecker* checker,
+ base::OnceCallback<void(const ClientStatus&)> callback) {
for (size_t i = 0; i < conditions_.size(); i++) {
checker->AddElementCheck(
conditions_[i].selector,
@@ -125,32 +126,53 @@ void WaitForDomAction::CheckElements(BatchElementChecker* checker,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
-void WaitForDomAction::OnSingleElementCheckDone(size_t condition_index,
- bool found) {
+void WaitForDomAction::OnSingleElementCheckDone(
+ size_t condition_index,
+ const ClientStatus& element_status) {
DCHECK(condition_index < conditions_.size());
Condition& condition = conditions_[condition_index];
- condition.match =
- condition.predicate == SelectorPredicate::kMatch ? found : !found;
+ condition.status_proto = element_status.proto_status();
+ condition.match = condition.predicate == SelectorPredicate::kMatch
+ ? element_status.ok()
+ : !element_status.ok();
// We can't stop here since the batch checker does not support stopping from a
// single element callback.
}
void WaitForDomAction::OnAllElementChecksDone(
- base::OnceCallback<void(bool)> callback) {
+ base::OnceCallback<void(const ClientStatus&)> callback) {
size_t match_count = 0;
+ ProcessedActionStatusProto last_error = UNKNOWN_ACTION_STATUS;
for (auto& condition : conditions_) {
if (condition.match) {
match_count++;
+ } else {
+ switch (condition.predicate) {
+ case SelectorPredicate::kMatch:
+ // For an expected match, return the status.
+ last_error = condition.status_proto;
+ break;
+ case SelectorPredicate::kNoMatch:
+ // For an expected non-match, return an ELEMENT_RESOLUTION_FAILED for
+ // lack of better status. We're expecting the element to not be there,
+ // but it is.
+ last_error = ELEMENT_RESOLUTION_FAILED;
+ break;
+
+ // Intentionally no default case to make compilation fail if a new
+ // value was added to the enum that is not treated not here.
+ }
}
}
bool success =
require_all_ ? match_count == conditions_.size() : match_count > 0;
- std::move(callback).Run(success);
+ std::move(callback).Run(success ? OkClientStatus()
+ : ClientStatus(last_error));
}
void WaitForDomAction::OnCheckDone(ProcessActionCallback callback,
- ProcessedActionStatusProto status) {
- UpdateProcessedAction(status);
+ const ClientStatus& status) {
+ UpdateProcessedAction(status.proto_status());
for (auto& condition : conditions_) {
if (condition.match && !condition.server_payload.empty()) {
processed_action_proto_->mutable_wait_for_dom_result()
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.h b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.h
index d56fa4c6191..45fe5e0c7ab 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.h
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -42,6 +43,9 @@ class WaitForDomAction : public Action {
// True if the condition matched.
bool match = false;
+ // Status proto result associated with this condition.
+ ProcessedActionStatusProto status_proto;
+
// A payload to report to the server when this condition match. Empty
// payloads are not reported.
std::string server_payload;
@@ -62,14 +66,16 @@ class WaitForDomAction : public Action {
const std::string& server_payload);
// Check all elements using the given BatchElementChecker and reports the
- // result to |callback|.
+ // result to |callback|. In case of failure, the last failed status is
+ // returned.
void CheckElements(BatchElementChecker* checker,
- base::OnceCallback<void(bool)> callback);
- void OnSingleElementCheckDone(size_t condition_index, bool result);
- void OnAllElementChecksDone(base::OnceCallback<void(bool)> callback);
+ base::OnceCallback<void(const ClientStatus&)> callback);
+ void OnSingleElementCheckDone(size_t condition_index,
+ const ClientStatus& element_status);
+ void OnAllElementChecksDone(
+ base::OnceCallback<void(const ClientStatus&)> callback);
- void OnCheckDone(ProcessActionCallback callback,
- ProcessedActionStatusProto status);
+ void OnCheckDone(ProcessActionCallback callback, const ClientStatus& status);
bool require_all_ = false;
std::vector<Condition> conditions_;
diff --git a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
index 15426de05de..9a3632f0c58 100644
--- a/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/actions/wait_for_dom_action_unittest.cc
@@ -30,7 +30,8 @@ class WaitForDomActionTest : public testing::Test {
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(_, _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(
+ RunOnceCallback<1>(ClientStatus(ELEMENT_RESOLUTION_FAILED)));
EXPECT_CALL(mock_action_delegate_, OnWaitForDom(_, _, _, _))
.WillRepeatedly(Invoke(this, &WaitForDomActionTest::FakeWaitForDom));
@@ -41,10 +42,10 @@ class WaitForDomActionTest : public testing::Test {
void FakeWaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>&
- check_elements,
- base::OnceCallback<void(ProcessedActionStatusProto)>& callback) {
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)>& check_elements,
+ base::OnceCallback<void(const ClientStatus&)>& callback) {
checker_ = std::make_unique<BatchElementChecker>();
has_check_elements_result_ = false;
check_elements.Run(
@@ -58,7 +59,7 @@ class WaitForDomActionTest : public testing::Test {
}
// Called from the check_elements callback passed to FakeWaitForDom.
- void OnCheckElementsDone(bool result) {
+ void OnCheckElementsDone(const ClientStatus& result) {
ASSERT_FALSE(has_check_elements_result_); // Duplicate calls
has_check_elements_result_ = true;
check_elements_result_ = result;
@@ -67,11 +68,10 @@ class WaitForDomActionTest : public testing::Test {
// Called by |checker_| once it's done. This ends the call to
// FakeWaitForDom().
void OnWaitForDomDone(
- base::OnceCallback<void(ProcessedActionStatusProto)> callback) {
+ base::OnceCallback<void(const ClientStatus&)> callback) {
ASSERT_TRUE(
has_check_elements_result_); // OnCheckElementsDone() not called
- std::move(callback).Run(check_elements_result_ ? ACTION_APPLIED
- : ELEMENT_RESOLUTION_FAILED);
+ std::move(callback).Run(check_elements_result_);
}
// Runs the action defined in |proto_| and reports the result to |callback_|.
@@ -88,7 +88,7 @@ class WaitForDomActionTest : public testing::Test {
WaitForDomProto proto_;
std::unique_ptr<BatchElementChecker> checker_;
bool has_check_elements_result_ = false;
- bool check_elements_result_ = false;
+ ClientStatus check_elements_result_;
};
TEST_F(WaitForDomActionTest, NoSelectors) {
@@ -100,7 +100,7 @@ TEST_F(WaitForDomActionTest, NoSelectors) {
TEST_F(WaitForDomActionTest, MatchOneElementFound) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
proto_.mutable_wait_until()->add_selectors("#element");
EXPECT_CALL(
@@ -118,7 +118,7 @@ TEST_F(WaitForDomActionTest, MatchOneElementNotFound) {
TEST_F(WaitForDomActionTest, NoMatchOneElementFound) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
proto_.mutable_wait_while()->add_selectors("#element");
EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
@@ -136,9 +136,9 @@ TEST_F(WaitForDomActionTest, NoMatchOneElementNotFound) {
TEST_F(WaitForDomActionTest, AllConditionsMetWantAll) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
proto_.mutable_wait_for_all()
->add_conditions()
->mutable_must_match()
@@ -155,9 +155,9 @@ TEST_F(WaitForDomActionTest, AllConditionsMetWantAll) {
TEST_F(WaitForDomActionTest, AllConditionsMetWantSome) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
proto_.mutable_wait_for_any()
->add_conditions()
->mutable_must_match()
@@ -234,13 +234,13 @@ TEST_F(WaitForDomActionTest, OnConditionMetWantSome) {
TEST_F(WaitForDomActionTest, ReportMatchesToServer) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element1"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element2"}), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element3"}), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Selector({"#element4"}), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
auto* condition1 = proto_.mutable_wait_for_any()->add_conditions();
condition1->mutable_must_match()->add_selectors("#element1");
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker.cc b/chromium/components/autofill_assistant/browser/batch_element_checker.cc
index 4cdad7c760c..b8aa97589f4 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker.cc
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker.cc
@@ -84,9 +84,9 @@ void BatchElementChecker::Run(WebController* web_controller) {
void BatchElementChecker::OnElementChecked(
std::vector<ElementCheckCallback>* callbacks,
- bool exists) {
+ const ClientStatus& element_status) {
for (auto& callback : *callbacks) {
- std::move(callback).Run(exists);
+ std::move(callback).Run(element_status);
}
callbacks->clear();
CheckDone();
@@ -94,10 +94,10 @@ void BatchElementChecker::OnElementChecked(
void BatchElementChecker::OnGetFieldValue(
std::vector<GetFieldValueCallback>* callbacks,
- bool exists,
+ const ClientStatus& element_status,
const std::string& value) {
for (auto& callback : *callbacks) {
- std::move(callback).Run(exists, value);
+ std::move(callback).Run(element_status, value);
}
callbacks->clear();
CheckDone();
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker.h b/chromium/components/autofill_assistant/browser/batch_element_checker.h
index 9cf893626ee..23670ec6354 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker.h
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker.h
@@ -15,6 +15,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/selector.h"
namespace autofill_assistant {
@@ -30,7 +31,7 @@ class BatchElementChecker {
// Callback for AddElementCheck. Argument is true if the check passed.
//
// An ElementCheckCallback must not delete its calling BatchElementChecker.
- using ElementCheckCallback = base::OnceCallback<void(bool)>;
+ using ElementCheckCallback = base::OnceCallback<void(const ClientStatus&)>;
// Callback for AddFieldValueCheck. Argument is true is the element exists.
// The string contains the field value, or an empty string if accessing the
@@ -38,7 +39,7 @@ class BatchElementChecker {
//
// An ElementCheckCallback must not delete its calling BatchElementChecker.
using GetFieldValueCallback =
- base::OnceCallback<void(bool, const std::string&)>;
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>;
// Checks an element.
//
@@ -68,9 +69,9 @@ class BatchElementChecker {
private:
void OnElementChecked(std::vector<ElementCheckCallback>* callbacks,
- bool exists);
+ const ClientStatus& element_status);
void OnGetFieldValue(std::vector<GetFieldValueCallback>* callbacks,
- bool exists,
+ const ClientStatus& element_status,
const std::string& value);
void CheckDone();
diff --git a/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc b/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
index 37695c241c5..dd72e03f02a 100644
--- a/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/batch_element_checker_unittest.cc
@@ -35,13 +35,14 @@ class BatchElementCheckerTest : public testing::Test {
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(_, _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
ON_CALL(mock_web_controller_, OnGetFieldValue(_, _))
- .WillByDefault(RunOnceCallback<1>(false, ""));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus(), ""));
}
- void OnElementExistenceCheck(const std::string& name, bool result) {
- element_exists_results_[name] = result;
+ void OnElementExistenceCheck(const std::string& name,
+ const ClientStatus& result) {
+ element_exists_results_[name] = result.ok();
}
BatchElementChecker::ElementCheckCallback ElementExistenceCallback(
@@ -50,8 +51,9 @@ class BatchElementCheckerTest : public testing::Test {
base::Unretained(this), name);
}
- void OnVisibilityRequirementCheck(const std::string& name, bool result) {
- element_visible_results_[name] = result;
+ void OnVisibilityRequirementCheck(const std::string& name,
+ const ClientStatus& result) {
+ element_visible_results_[name] = result.ok();
}
BatchElementChecker::ElementCheckCallback VisibilityRequirementCallback(
@@ -62,7 +64,7 @@ class BatchElementCheckerTest : public testing::Test {
}
void OnFieldValueCheck(const std::string& name,
- bool exists,
+ const ClientStatus& result,
const std::string& value) {
get_field_value_results_[name] = value;
}
@@ -104,7 +106,7 @@ TEST_F(BatchElementCheckerTest, Empty) {
TEST_F(BatchElementCheckerTest, OneElementFound) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"exists"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
checks_.AddElementCheck(Selector({"exists"}),
ElementExistenceCallback("exists"));
Run("was_run");
@@ -116,7 +118,7 @@ TEST_F(BatchElementCheckerTest, OneElementFound) {
TEST_F(BatchElementCheckerTest, OneElementNotFound) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"does_not_exist"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
checks_.AddElementCheck(Selector({"does_not_exist"}),
ElementExistenceCallback("does_not_exist"));
Run("was_run");
@@ -127,7 +129,7 @@ TEST_F(BatchElementCheckerTest, OneElementNotFound) {
TEST_F(BatchElementCheckerTest, OneFieldValueFound) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
- .WillOnce(RunOnceCallback<1>(true, "some value"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "some value"));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
Run("was_run");
@@ -137,7 +139,7 @@ TEST_F(BatchElementCheckerTest, OneFieldValueFound) {
TEST_F(BatchElementCheckerTest, OneFieldValueNotFound) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
- .WillOnce(RunOnceCallback<1>(false, ""));
+ .WillOnce(RunOnceCallback<1>(ClientStatus(), ""));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
Run("was_run");
@@ -147,7 +149,7 @@ TEST_F(BatchElementCheckerTest, OneFieldValueNotFound) {
TEST_F(BatchElementCheckerTest, OneFieldValueEmpty) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
- .WillOnce(RunOnceCallback<1>(true, ""));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
Run("was_run");
@@ -157,15 +159,15 @@ TEST_F(BatchElementCheckerTest, OneFieldValueEmpty) {
TEST_F(BatchElementCheckerTest, MultipleElements) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"1"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"2"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"3"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"4"})), _))
- .WillOnce(RunOnceCallback<1>(true, "value"));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus(), "value"));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"5"})), _))
- .WillOnce(RunOnceCallback<1>(false, ""));
+ .WillOnce(RunOnceCallback<1>(ClientStatus(), ""));
checks_.AddElementCheck(Selector({"1"}), ElementExistenceCallback("1"));
checks_.AddElementCheck(Selector({"2"}), ElementExistenceCallback("2"));
@@ -184,9 +186,9 @@ TEST_F(BatchElementCheckerTest, MultipleElements) {
TEST_F(BatchElementCheckerTest, DeduplicateElementExists) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"1"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"2"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
checks_.AddElementCheck(Selector({"1"}), ElementExistenceCallback("first 1"));
checks_.AddElementCheck(Selector({"1"}),
@@ -204,10 +206,10 @@ TEST_F(BatchElementCheckerTest, DeduplicateElementExists) {
TEST_F(BatchElementCheckerTest, DeduplicateElementVisible) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"1"}).MustBeVisible()), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"2"}).MustBeVisible()), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
checks_.AddElementCheck(Selector({"1"}).MustBeVisible(),
VisibilityRequirementCallback("first 1"));
diff --git a/chromium/components/autofill_assistant/browser/client_memory.cc b/chromium/components/autofill_assistant/browser/client_memory.cc
index 2f7dfcdebb1..579015045c3 100644
--- a/chromium/components/autofill_assistant/browser/client_memory.cc
+++ b/chromium/components/autofill_assistant/browser/client_memory.cc
@@ -20,7 +20,7 @@ const autofill::CreditCard* ClientMemory::selected_card() const {
}
bool ClientMemory::has_selected_card() const {
- return selected_card_.has_value();
+ return selected_card() != nullptr;
}
const autofill::AutofillProfile* ClientMemory::selected_address(
@@ -44,7 +44,7 @@ void ClientMemory::set_selected_address(
}
bool ClientMemory::has_selected_address(const std::string& name) const {
- return selected_addresses_.find(name) != selected_addresses_.end();
+ return selected_address(name) != nullptr;
}
void ClientMemory::set_selected_login(const WebsiteLoginFetcher::Login& login) {
@@ -52,7 +52,7 @@ void ClientMemory::set_selected_login(const WebsiteLoginFetcher::Login& login) {
}
bool ClientMemory::has_selected_login() const {
- return selected_login_.has_value();
+ return selected_login() != nullptr;
}
const WebsiteLoginFetcher::Login* ClientMemory::selected_login() const {
@@ -62,6 +62,23 @@ const WebsiteLoginFetcher::Login* ClientMemory::selected_login() const {
return nullptr;
}
+const std::string* ClientMemory::additional_value(const std::string& key) {
+ auto it = additional_values_.find(key);
+ if (it == additional_values_.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+bool ClientMemory::has_additional_value(const std::string& key) {
+ return additional_values_.find(key) != additional_values_.end();
+}
+
+void ClientMemory::set_additional_value(const std::string& key,
+ const std::string& value) {
+ additional_values_[key] = value;
+}
+
std::string ClientMemory::GetAllAddressKeyNames() const {
std::vector<std::string> entries;
for (const auto& entry : selected_addresses_) {
diff --git a/chromium/components/autofill_assistant/browser/client_memory.h b/chromium/components/autofill_assistant/browser/client_memory.h
index ee24fb35d0a..b3e4af6acab 100644
--- a/chromium/components/autofill_assistant/browser/client_memory.h
+++ b/chromium/components/autofill_assistant/browser/client_memory.h
@@ -55,6 +55,15 @@ class ClientMemory {
// The selected login or nullptr if no login was selected.
const WebsiteLoginFetcher::Login* selected_login() const;
+ // The additional value for |key|, or nullptr if it does not exist.
+ const std::string* additional_value(const std::string& key);
+
+ // Returns true if an additional value is stored for |key|.
+ bool has_additional_value(const std::string& key);
+
+ // Sets the additional value for |key|.
+ void set_additional_value(const std::string& key, const std::string& value);
+
std::string GetAllAddressKeyNames() const;
private:
@@ -64,6 +73,8 @@ class ClientMemory {
// The address key requested by the autofill action.
std::map<std::string, std::unique_ptr<autofill::AutofillProfile>>
selected_addresses_;
+ // Maps keys to additional values.
+ std::map<std::string, std::string> additional_values_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/client_settings.cc b/chromium/components/autofill_assistant/browser/client_settings.cc
index f375b67fdbd..ecccc89bcb8 100644
--- a/chromium/components/autofill_assistant/browser/client_settings.cc
+++ b/chromium/components/autofill_assistant/browser/client_settings.cc
@@ -4,11 +4,30 @@
#include "components/autofill_assistant/browser/client_settings.h"
-#include "components/autofill_assistant/browser/service.pb.h"
+namespace {
+
+bool IsValidOverlayImageProto(
+ const autofill_assistant::OverlayImageProto& proto) {
+ if (!proto.image_url().empty() && !proto.has_image_size()) {
+ DVLOG(1) << __func__ << ": Missing image_size in overlay_image, ignoring";
+ return false;
+ }
+
+ if (!proto.text().empty() &&
+ (!proto.has_text_color() || !proto.has_text_size())) {
+ DVLOG(1) << __func__
+ << ": Missing text_color or text_size in overlay_image, ignoring";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
namespace autofill_assistant {
ClientSettings::ClientSettings() = default;
+ClientSettings::~ClientSettings() = default;
void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
if (proto.has_periodic_script_check_interval_ms()) {
@@ -44,13 +63,6 @@ void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
if (proto.has_document_ready_check_count()) {
document_ready_check_count = proto.document_ready_check_count();
}
- if (proto.has_enable_graceful_shutdown()) {
- enable_graceful_shutdown = proto.enable_graceful_shutdown();
- }
- if (proto.has_graceful_shutdown_delay_ms()) {
- graceful_shutdown_delay =
- base::TimeDelta::FromMilliseconds(proto.graceful_shutdown_delay_ms());
- }
if (proto.has_cancel_delay_ms()) {
cancel_delay = base::TimeDelta::FromMilliseconds(proto.cancel_delay_ms());
}
@@ -65,6 +77,12 @@ void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
tap_shutdown_delay =
base::TimeDelta::FromMilliseconds(proto.tap_shutdown_delay_ms());
}
+ if (proto.has_overlay_image() &&
+ IsValidOverlayImageProto(proto.overlay_image())) {
+ overlay_image = proto.overlay_image();
+ } else {
+ overlay_image.reset();
+ }
}
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/client_settings.h b/chromium/components/autofill_assistant/browser/client_settings.h
index f007b58db5c..1968670c2e5 100644
--- a/chromium/components/autofill_assistant/browser/client_settings.h
+++ b/chromium/components/autofill_assistant/browser/client_settings.h
@@ -6,10 +6,11 @@
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_SETTINGS_H_
#include "base/macros.h"
+#include "base/optional.h"
#include "base/time/time.h"
+#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
-class ClientSettingsProto;
// Global settings for the Autofill Assistant client.
//
@@ -20,6 +21,7 @@ class ClientSettingsProto;
// pointer to the single ClientSettings instance instead of making a copy.
struct ClientSettings {
ClientSettings();
+ ~ClientSettings();
// Time between two periodic script precondition checks.
base::TimeDelta periodic_script_check_interval =
@@ -61,14 +63,6 @@ struct ClientSettings {
// ready.
int document_ready_check_count = 50;
- // Whether graceful shutdown should be enabled. If false, the UI stays
- // up until it's dismissed.
- bool enable_graceful_shutdown = true;
-
- // How long to wait before shutting down during graceful shutdown. If 0
- // shutdown happens immediately.
- base::TimeDelta graceful_shutdown_delay = base::TimeDelta::FromSeconds(5);
-
// How much time to give users to tap undo when they tap a cancel button.
base::TimeDelta cancel_delay = base::TimeDelta::FromSeconds(5);
@@ -84,6 +78,9 @@ struct ClientSettings {
// taps where
base::TimeDelta tap_shutdown_delay = base::TimeDelta::FromSeconds(5);
+ // Optional image drawn on top of overlays.
+ base::Optional<OverlayImageProto> overlay_image;
+
void UpdateFromProto(const ClientSettingsProto& proto);
private:
diff --git a/chromium/components/autofill_assistant/browser/client_status.cc b/chromium/components/autofill_assistant/browser/client_status.cc
index 6ee79da9bef..710d2d34a51 100644
--- a/chromium/components/autofill_assistant/browser/client_status.cc
+++ b/chromium/components/autofill_assistant/browser/client_status.cc
@@ -77,54 +77,45 @@ std::ostream& operator<<(std::ostream& out,
case ProcessedActionStatusProto::USER_ABORTED_ACTION:
out << "USER_ABORTED_ACTION";
break;
-
case ProcessedActionStatusProto::GET_FULL_CARD_FAILED:
out << "GET_FULL_CARD_FAILED";
break;
-
case ProcessedActionStatusProto::PRECONDITION_FAILED:
out << "PRECONDITION_FAILED";
break;
-
case ProcessedActionStatusProto::INVALID_ACTION:
out << "INVALID_ACTION";
break;
-
case ProcessedActionStatusProto::UNSUPPORTED:
out << "UNSUPPORTED";
break;
-
case ProcessedActionStatusProto::TIMED_OUT:
out << "TIMED_OUT";
break;
-
case ProcessedActionStatusProto::ELEMENT_UNSTABLE:
out << "ELEMENT_UNSTABLE";
break;
-
case ProcessedActionStatusProto::INVALID_SELECTOR:
out << "INVALID_SELECTOR";
break;
-
case ProcessedActionStatusProto::OPTION_VALUE_NOT_FOUND:
out << "OPTION_VALUE_NOT_FOUND";
break;
-
case ProcessedActionStatusProto::UNEXPECTED_JS_ERROR:
out << "UNEXPECTED_JS_ERROR";
break;
-
case ProcessedActionStatusProto::TOO_MANY_ELEMENTS:
out << "TOO_MANY_ELEMENTS";
break;
-
case ProcessedActionStatusProto::NAVIGATION_ERROR:
out << "NAVIGATION_ERROR";
break;
-
case ProcessedActionStatusProto::AUTOFILL_INFO_NOT_AVAILABLE:
out << "AUTOFILL_INFO_NOT_AVAILABLE";
break;
+ case ProcessedActionStatusProto::FRAME_HOST_NOT_FOUND:
+ out << "FRAME_HOST_NOT_FOUND";
+ break;
// Intentionally no default case to make compilation fail if a new value
// was added to the enum but not to this list.
diff --git a/chromium/components/autofill_assistant/browser/controller.cc b/chromium/components/autofill_assistant/browser/controller.cc
index d1343ff116a..602d2c9dab0 100644
--- a/chromium/components/autofill_assistant/browser/controller.cc
+++ b/chromium/components/autofill_assistant/browser/controller.cc
@@ -85,6 +85,22 @@ bool StateEndsFlow(AutofillAssistantState state) {
return false;
}
+// Convenience method to set all fields of a |DateTimeProto|.
+void SetDateTimeProto(DateTimeProto* proto,
+ int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) {
+ proto->mutable_date()->set_year(year);
+ proto->mutable_date()->set_month(month);
+ proto->mutable_date()->set_day(day);
+ proto->mutable_time()->set_hour(hour);
+ proto->mutable_time()->set_minute(minute);
+ proto->mutable_time()->set_second(second);
+}
+
} // namespace
Controller::Controller(content::WebContents* web_contents,
@@ -968,6 +984,57 @@ void Controller::OnTermsAndConditionsLinkClicked(int link) {
std::move(callback).Run(link);
}
+void Controller::SetDateTimeRangeStart(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) {
+ if (!user_data_)
+ return;
+
+ SetDateTimeProto(&user_data_->date_time_range_start, year, month, day, hour,
+ minute, second);
+ for (ControllerObserver& observer : observers_) {
+ observer.OnUserDataChanged(user_data_.get());
+ }
+ UpdateCollectUserDataActions();
+}
+
+void Controller::SetDateTimeRangeEnd(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) {
+ if (!user_data_)
+ return;
+
+ SetDateTimeProto(&user_data_->date_time_range_end, year, month, day, hour,
+ minute, second);
+ for (ControllerObserver& observer : observers_) {
+ observer.OnUserDataChanged(user_data_.get());
+ }
+ UpdateCollectUserDataActions();
+}
+
+void Controller::SetAdditionalValue(const std::string& client_memory_key,
+ const std::string& value) {
+ if (!user_data_)
+ return;
+ auto it = user_data_->additional_values_to_store.find(client_memory_key);
+ if (it == user_data_->additional_values_to_store.end()) {
+ NOTREACHED() << client_memory_key << " not found";
+ return;
+ }
+ it->second.assign(value);
+ for (ControllerObserver& observer : observers_) {
+ observer.OnUserDataChanged(user_data_.get());
+ }
+ // It is currently not necessary to call |UpdateCollectUserDataActions|
+ // because all additional values are optional.
+}
+
void Controller::SetShippingAddress(
std::unique_ptr<autofill::AutofillProfile> address) {
if (!user_data_)
@@ -992,23 +1059,13 @@ void Controller::SetContactInfo(
UpdateCollectUserDataActions();
}
-void Controller::SetCreditCard(std::unique_ptr<autofill::CreditCard> card) {
+void Controller::SetCreditCard(
+ std::unique_ptr<autofill::CreditCard> card,
+ std::unique_ptr<autofill::AutofillProfile> billing_profile) {
if (!user_data_)
return;
- autofill::AutofillProfile* billing_profile =
- !card || card->billing_address_id().empty()
- ? nullptr
- : GetPersonalDataManager()->GetProfileByGUID(
- card->billing_address_id());
- if (billing_profile) {
- auto billing_address =
- std::make_unique<autofill::AutofillProfile>(*billing_profile);
- user_data_->billing_address = std::move(billing_address);
- } else {
- user_data_->billing_address.reset();
- }
-
+ user_data_->billing_address = std::move(billing_profile);
user_data_->card = std::move(card);
for (ControllerObserver& observer : observers_) {
observer.OnUserDataChanged(user_data_.get());
@@ -1050,7 +1107,7 @@ void Controller::UpdateCollectUserDataActions() {
}
bool confirm_button_enabled = CollectUserDataAction::IsUserDataComplete(
- GetPersonalDataManager(), *user_data_, *collect_user_data_options_);
+ *user_data_, *collect_user_data_options_);
UserAction confirm(collect_user_data_options_->confirm_action);
confirm.SetEnabled(confirm_button_enabled);
diff --git a/chromium/components/autofill_assistant/browser/controller.h b/chromium/components/autofill_assistant/browser/controller.h
index a7777502fb3..9554a6ae3a8 100644
--- a/chromium/components/autofill_assistant/browser/controller.h
+++ b/chromium/components/autofill_assistant/browser/controller.h
@@ -152,11 +152,27 @@ class Controller : public ScriptExecutorDelegate,
std::unique_ptr<autofill::AutofillProfile> address) override;
void SetContactInfo(
std::unique_ptr<autofill::AutofillProfile> profile) override;
- void SetCreditCard(std::unique_ptr<autofill::CreditCard> card) override;
+ void SetCreditCard(
+ std::unique_ptr<autofill::CreditCard> card,
+ std::unique_ptr<autofill::AutofillProfile> billing_profile) override;
void SetTermsAndConditions(
TermsAndConditionsState terms_and_conditions) override;
void SetLoginOption(std::string identifier) override;
void OnTermsAndConditionsLinkClicked(int link) override;
+ void SetDateTimeRangeStart(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) override;
+ void SetDateTimeRangeEnd(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) override;
+ void SetAdditionalValue(const std::string& client_memory_key,
+ const std::string& value) override;
void GetTouchableArea(std::vector<RectF>* area) const override;
void GetRestrictedArea(std::vector<RectF>* area) const override;
void GetVisualViewport(RectF* visual_viewport) const override;
diff --git a/chromium/components/autofill_assistant/browser/controller_observer.cc b/chromium/components/autofill_assistant/browser/controller_observer.cc
index 15fd36975ea..f951da6e0ea 100644
--- a/chromium/components/autofill_assistant/browser/controller_observer.cc
+++ b/chromium/components/autofill_assistant/browser/controller_observer.cc
@@ -11,31 +11,4 @@ namespace autofill_assistant {
ControllerObserver::ControllerObserver() = default;
ControllerObserver::~ControllerObserver() = default;
-
-void ControllerObserver::OnStateChanged(AutofillAssistantState new_state) {}
-void ControllerObserver::OnStatusMessageChanged(const std::string& message) {}
-void ControllerObserver::OnBubbleMessageChanged(const std::string& message) {}
-void ControllerObserver::CloseCustomTab() {}
-void ControllerObserver::OnUserActionsChanged(
- const std::vector<UserAction>& user_actions) {}
-void ControllerObserver::OnCollectUserDataOptionsChanged(
- const CollectUserDataOptions* options) {}
-void ControllerObserver::OnUserDataChanged(const UserData* state) {}
-void ControllerObserver::OnDetailsChanged(const Details* details) {}
-void ControllerObserver::OnInfoBoxChanged(const InfoBox* info_box) {}
-void ControllerObserver::OnProgressChanged(int progress) {}
-void ControllerObserver::OnProgressVisibilityChanged(bool visible) {}
-void ControllerObserver::OnTouchableAreaChanged(
- const RectF& visual_viewport,
- const std::vector<RectF>& touchable_areas,
- const std::vector<RectF>& restricted_areas) {}
-void ControllerObserver::OnViewportModeChanged(ViewportMode mode) {}
-void ControllerObserver::OnPeekModeChanged(
- ConfigureBottomSheetProto::PeekMode peek_mode) {}
-void ControllerObserver::OnOverlayColorsChanged(
- const UiDelegate::OverlayColors& colors) {}
-void ControllerObserver::OnFormChanged(const FormProto* form) {}
-void ControllerObserver::OnClientSettingsChanged(
- const ClientSettings& settings) {}
-
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/controller_observer.h b/chromium/components/autofill_assistant/browser/controller_observer.h
index 2c57627757a..82b93a10aca 100644
--- a/chromium/components/autofill_assistant/browser/controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/controller_observer.h
@@ -31,44 +31,44 @@ class ControllerObserver : public base::CheckedObserver {
~ControllerObserver() override;
// Called when the controller has entered a new state.
- virtual void OnStateChanged(AutofillAssistantState new_state);
+ virtual void OnStateChanged(AutofillAssistantState new_state) = 0;
// Report that the status message has changed.
- virtual void OnStatusMessageChanged(const std::string& message);
+ virtual void OnStatusMessageChanged(const std::string& message) = 0;
// Report that the bubble / tooltip message has changed.
- virtual void OnBubbleMessageChanged(const std::string& message);
+ virtual void OnBubbleMessageChanged(const std::string& message) = 0;
// If the current chrome activity is a custom tab activity, close it.
// Otherwise, do nothing.
- virtual void CloseCustomTab();
+ virtual void CloseCustomTab() = 0;
// Report that the set of user actions has changed.
virtual void OnUserActionsChanged(
- const std::vector<UserAction>& user_actions);
+ const std::vector<UserAction>& user_actions) = 0;
// Report that the options configuring a CollectUserDataAction have changed.
virtual void OnCollectUserDataOptionsChanged(
- const CollectUserDataOptions* options);
+ const CollectUserDataOptions* options) = 0;
// Updates the currently selected user data (e.g., contact information).
- virtual void OnUserDataChanged(const UserData* state);
+ virtual void OnUserDataChanged(const UserData* state) = 0;
// Called when details have changed. Details will be null if they have been
// cleared.
- virtual void OnDetailsChanged(const Details* details);
+ virtual void OnDetailsChanged(const Details* details) = 0;
// Called when info box has changed. |info_box| will be null if it has been
// cleared.
- virtual void OnInfoBoxChanged(const InfoBox* info_box);
+ virtual void OnInfoBoxChanged(const InfoBox* info_box) = 0;
// Called when the current progress has changed. Progress, is expressed as a
// percentage.
- virtual void OnProgressChanged(int progress);
+ virtual void OnProgressChanged(int progress) = 0;
// Called when the current progress bar visibility has changed. If |visible|
// is true, then the bar is now shown.
- virtual void OnProgressVisibilityChanged(bool visible);
+ virtual void OnProgressVisibilityChanged(bool visible) = 0;
// Updates the area of the visible viewport that is accessible when the
// overlay state is OverlayState::PARTIAL.
@@ -88,22 +88,24 @@ class ControllerObserver : public base::CheckedObserver {
virtual void OnTouchableAreaChanged(
const RectF& visual_viewport,
const std::vector<RectF>& touchable_areas,
- const std::vector<RectF>& restricted_areas);
+ const std::vector<RectF>& restricted_areas) = 0;
// Called when the viewport mode has changed.
- virtual void OnViewportModeChanged(ViewportMode mode);
+ virtual void OnViewportModeChanged(ViewportMode mode) = 0;
// Called when the peek mode has changed.
- virtual void OnPeekModeChanged(ConfigureBottomSheetProto::PeekMode peek_mode);
+ virtual void OnPeekModeChanged(
+ ConfigureBottomSheetProto::PeekMode peek_mode) = 0;
// Called when the overlay colors have changed.
- virtual void OnOverlayColorsChanged(const UiDelegate::OverlayColors& colors);
+ virtual void OnOverlayColorsChanged(
+ const UiDelegate::OverlayColors& colors) = 0;
// Called when the form has changed.
- virtual void OnFormChanged(const FormProto* form);
+ virtual void OnFormChanged(const FormProto* form) = 0;
// Called when client settings have changed.
- virtual void OnClientSettingsChanged(const ClientSettings& settings);
+ virtual void OnClientSettingsChanged(const ClientSettings& settings) = 0;
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CONTROLLER_OBSERVER_H_
diff --git a/chromium/components/autofill_assistant/browser/controller_unittest.cc b/chromium/components/autofill_assistant/browser/controller_unittest.cc
index 66e4249393a..8c4613af003 100644
--- a/chromium/components/autofill_assistant/browser/controller_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/controller_unittest.cc
@@ -8,8 +8,8 @@
#include <utility>
#include "base/guid.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/test/gmock_callback_support.h"
+#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
@@ -82,11 +82,11 @@ struct MockCollectUserDataOptions : public CollectUserDataOptions {
MockCollectUserDataOptions() {
base::MockOnceCallback<void(std::unique_ptr<UserData>)>
mock_confirm_callback;
- confirm_callback = std::move(mock_confirm_callback.Get());
+ confirm_callback = mock_confirm_callback.Get();
base::MockOnceCallback<void(int)> mock_actions_callback;
- additional_actions_callback = std::move(mock_actions_callback.Get());
+ additional_actions_callback = mock_actions_callback.Get();
base::MockOnceCallback<void(int)> mock_terms_callback;
- terms_link_callback = std::move(mock_terms_callback.Get());
+ terms_link_callback = mock_terms_callback.Get();
}
};
@@ -127,7 +127,7 @@ class ControllerTest : public content::RenderViewHostTestHarness {
.WillByDefault(RunOnceCallback<4>(true, ""));
ON_CALL(*mock_web_controller_, OnElementCheck(_, _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
ON_CALL(mock_observer_, OnStateChanged(_))
.WillByDefault(Invoke([this](AutofillAssistantState state) {
@@ -522,6 +522,28 @@ TEST_F(ControllerTest, Stop) {
EXPECT_TRUE(controller_->PerformUserAction(0));
}
+TEST_F(ControllerTest, CloseCustomTab) {
+ SupportsScriptResponseProto script_response;
+ AddRunnableScript(&script_response, "stop");
+ SetNextScriptResponse(script_response);
+
+ ActionsResponseProto actions_response;
+ actions_response.add_actions()->mutable_stop()->set_close_cct(true);
+ std::string actions_response_str;
+ actions_response.SerializeToString(&actions_response_str);
+ EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _, _, _, _))
+ .WillOnce(RunOnceCallback<5>(true, actions_response_str));
+
+ Start();
+ ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
+ EXPECT_CALL(mock_observer_, CloseCustomTab()).Times(1);
+
+ testing::InSequence seq;
+ EXPECT_CALL(fake_client_,
+ Shutdown(Metrics::DropOutReason::CUSTOM_TAB_CLOSED));
+ EXPECT_TRUE(controller_->PerformUserAction(0));
+}
+
TEST_F(ControllerTest, Reset) {
// 1. Fetch scripts for URL, which in contains a single "reset" script.
SupportsScriptResponseProto script_response;
@@ -740,7 +762,7 @@ TEST_F(ControllerTest, KeepCheckingForElement) {
}
EXPECT_CALL(*mock_web_controller_, OnElementCheck(_, _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
@@ -1484,7 +1506,8 @@ TEST_F(ControllerTest, UserDataFormCreditCard) {
Property(&UserAction::enabled, Eq(false)))))
.Times(1);
controller_->SetCreditCard(
- std::make_unique<autofill::CreditCard>(*credit_card));
+ std::make_unique<autofill::CreditCard>(*credit_card),
+ /* billing_profile =*/nullptr);
// Credit card with valid billing address is ok.
auto billing_address = std::make_unique<autofill::AutofillProfile>(
@@ -1494,15 +1517,16 @@ TEST_F(ControllerTest, UserDataFormCreditCard) {
"123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "16505678910");
credit_card->set_billing_address_id(billing_address->guid());
- ON_CALL(*fake_client_.GetPersonalDataManager(),
- GetProfileByGUID(billing_address->guid()))
- .WillByDefault(Return(billing_address.get()));
EXPECT_CALL(mock_observer_, OnUserActionsChanged(UnorderedElementsAre(
Property(&UserAction::enabled, Eq(true)))))
.Times(1);
controller_->SetCreditCard(
- std::make_unique<autofill::CreditCard>(*credit_card));
+ std::make_unique<autofill::CreditCard>(*credit_card),
+ std::make_unique<autofill::AutofillProfile>(*billing_address));
EXPECT_THAT(controller_->GetUserData()->card->Compare(*credit_card), Eq(0));
+ EXPECT_THAT(
+ controller_->GetUserData()->billing_address->Compare(*billing_address),
+ Eq(0));
}
TEST_F(ControllerTest, SetTermsAndConditions) {
@@ -1575,4 +1599,58 @@ TEST_F(ControllerTest, SetShippingAddress) {
Eq(0));
}
+TEST_F(ControllerTest, SetAdditionalValues) {
+ auto options = std::make_unique<MockCollectUserDataOptions>();
+ auto user_data = std::make_unique<UserData>();
+
+ user_data->additional_values_to_store["key1"] = "123456789";
+ user_data->additional_values_to_store["key2"] = "";
+ user_data->additional_values_to_store["key3"] = "";
+
+ testing::InSequence seq;
+ EXPECT_CALL(mock_observer_, OnUserActionsChanged(UnorderedElementsAre(
+ Property(&UserAction::enabled, Eq(true)))))
+ .Times(1);
+ controller_->SetCollectUserDataOptions(std::move(options),
+ std::move(user_data));
+
+ EXPECT_CALL(mock_observer_, OnUserDataChanged(Not(nullptr))).Times(2);
+ controller_->SetAdditionalValue("key2", "value2");
+ controller_->SetAdditionalValue("key3", "value3");
+ EXPECT_EQ(controller_->GetUserData()->additional_values_to_store.at("key1"),
+ "123456789");
+ EXPECT_EQ(controller_->GetUserData()->additional_values_to_store.at("key2"),
+ "value2");
+ EXPECT_EQ(controller_->GetUserData()->additional_values_to_store.at("key3"),
+ "value3");
+ EXPECT_DCHECK_DEATH(controller_->SetAdditionalValue("key4", "someValue"));
+}
+
+TEST_F(ControllerTest, SetOverlayColors) {
+ EXPECT_CALL(
+ mock_observer_,
+ OnOverlayColorsChanged(AllOf(
+ Field(&Controller::OverlayColors::background, StrEq("#FF000000")),
+ Field(&Controller::OverlayColors::highlight_border,
+ StrEq("#FFFFFFFF")))));
+
+ std::map<std::string, std::string> parameters;
+ parameters["OVERLAY_COLORS"] = "#FF000000:#FFFFFFFF";
+ auto context = TriggerContext::Create(parameters, "exps");
+
+ GURL url("http://a.example.com/path");
+ controller_->Start(url, std::move(context));
+}
+
+TEST_F(ControllerTest, ChangeClientSettings) {
+ SupportsScriptResponseProto response;
+ response.mutable_client_settings()->set_periodic_script_check_interval_ms(1);
+ SetupScripts(response);
+ EXPECT_CALL(mock_observer_,
+ OnClientSettingsChanged(
+ Field(&ClientSettings::periodic_script_check_interval,
+ base::TimeDelta::FromMilliseconds(1))));
+ Start();
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/details.cc b/chromium/components/autofill_assistant/browser/details.cc
index 294d566fced..0b5b66d4862 100644
--- a/chromium/components/autofill_assistant/browser/details.cc
+++ b/chromium/components/autofill_assistant/browser/details.cc
@@ -7,18 +7,80 @@
#include <unordered_set>
#include <base/strings/stringprintf.h>
+#include "base/i18n/time_formatting.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
#include "components/autofill/core/browser/geo/country_names.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/strings/grit/components_strings.h"
+#include "third_party/re2/src/re2/re2.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill_assistant {
+namespace {
+// TODO(b/141850276): Remove hardcoded formatting strings.
+constexpr char kDateFormat[] = "EEE, MMM d";
+constexpr char kTimeFormat[] = "h:mm a";
+constexpr char kDateTimeSeparator[] = " \xE2\x80\xA2 ";
constexpr char kSpaceBetweenCardNumAndDate[] = " ";
+// Parse RFC 3339 date-time. Store the value in the datetime proto field.
+bool ParseDateTimeStringToProto(const std::string& datetime,
+ DateTimeProto* result) {
+ // RFC 3339 format without timezone: yyyy'-'MM'-'dd'T'HH':'mm':'ss
+ std::string pattern =
+ R"rgx((\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}))rgx";
+
+ int year, month, day, hour, minute, second;
+ if (re2::RE2::FullMatch(datetime, pattern, &year, &month, &day, &hour,
+ &minute, &second)) {
+ auto* date = result->mutable_date();
+ date->set_year(year);
+ date->set_month(month);
+ date->set_day(day);
+ auto* time = result->mutable_time();
+ time->set_hour(hour);
+ time->set_minute(minute);
+ time->set_second(second);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Format a datetime proto with current locale.
+std::string FormatDateTimeProto(const DateTimeProto& date_time) {
+ if (!date_time.has_date() || !date_time.has_time()) {
+ return std::string();
+ }
+ auto date_proto = date_time.date();
+ auto time_proto = date_time.time();
+
+ base::Time::Exploded exploded_time = {
+ date_proto.year(), date_proto.month(),
+ /* day_of_week = */ -1, date_proto.day(), time_proto.hour(),
+ time_proto.minute(), time_proto.second(), 0};
+ base::Time time;
+
+ if (base::Time::FromLocalExploded(exploded_time, &time)) {
+ auto date_string = base::TimeFormatWithPattern(time, kDateFormat);
+ auto time_string = base::TimeFormatWithPattern(time, kTimeFormat);
+
+ return base::StrCat({base::UTF16ToUTF8(time_string), kDateTimeSeparator,
+ base::UTF16ToUTF8(date_string)});
+ }
+
+ return std::string();
+}
+
+} // namespace
+
+Details::Details() = default;
+Details::~Details() = default;
+
// static
bool Details::UpdateFromProto(const ShowDetailsProto& proto, Details* details) {
if (!proto.has_details()) {
@@ -125,52 +187,45 @@ bool Details::UpdateFromSelectedCreditCard(const ShowDetailsProto& proto,
base::Value Details::GetDebugContext() const {
base::Value dict(base::Value::Type::DICTIONARY);
- if (!details_proto().title().empty())
- dict.SetKey("title", base::Value(details_proto().title()));
-
- if (!details_proto().image_url().empty())
- dict.SetKey("image_url", base::Value(details_proto().image_url()));
-
- if (!details_proto().total_price().empty())
- dict.SetKey("total_price", base::Value(details_proto().total_price()));
-
- if (!details_proto().total_price_label().empty())
- dict.SetKey("total_price_label",
- base::Value(details_proto().total_price_label()));
-
- if (!details_proto().description_line_1().empty())
- dict.SetKey("description_line_1",
- base::Value(details_proto().description_line_1()));
-
- if (!details_proto().description_line_2().empty())
- dict.SetKey("description_line_2",
- base::Value(details_proto().description_line_2()));
-
- if (!details_proto().description_line_3().empty())
- dict.SetKey("description_line_3",
- base::Value(details_proto().description_line_3()));
-
- if (details_proto().has_datetime()) {
- dict.SetKey("datetime",
- base::Value(base::StringPrintf(
- "%d-%02d-%02dT%02d:%02d:%02d",
- static_cast<int>(details_proto().datetime().date().year()),
- details_proto().datetime().date().month(),
- details_proto().datetime().date().day(),
- details_proto().datetime().time().hour(),
- details_proto().datetime().time().minute(),
- details_proto().datetime().time().second())));
+ if (!proto_.title().empty())
+ dict.SetKey("title", base::Value(proto_.title()));
+
+ if (!proto_.image_url().empty())
+ dict.SetKey("image_url", base::Value(proto_.image_url()));
+
+ if (!proto_.total_price().empty())
+ dict.SetKey("total_price", base::Value(proto_.total_price()));
+
+ if (!proto_.total_price_label().empty())
+ dict.SetKey("total_price_label", base::Value(proto_.total_price_label()));
+
+ if (!proto_.description_line_1().empty())
+ dict.SetKey("description_line_1", base::Value(proto_.description_line_1()));
+
+ if (!proto_.description_line_2().empty())
+ dict.SetKey("description_line_2", base::Value(proto_.description_line_2()));
+
+ if (!proto_.description_line_3().empty())
+ dict.SetKey("description_line_3", base::Value(proto_.description_line_3()));
+
+ if (proto_.has_datetime()) {
+ dict.SetKey(
+ "datetime",
+ base::Value(base::StringPrintf(
+ "%d-%02d-%02dT%02d:%02d:%02d",
+ static_cast<int>(proto_.datetime().date().year()),
+ proto_.datetime().date().month(), proto_.datetime().date().day(),
+ proto_.datetime().time().hour(), proto_.datetime().time().minute(),
+ proto_.datetime().time().second())));
}
- if (!datetime_.empty())
- dict.SetKey("datetime_str", base::Value(datetime_));
dict.SetKey("user_approval_required",
- base::Value(changes().user_approval_required()));
- dict.SetKey("highlight_title", base::Value(changes().highlight_title()));
- dict.SetKey("highlight_line1", base::Value(changes().highlight_line1()));
- dict.SetKey("highlight_line2", base::Value(changes().highlight_line2()));
- dict.SetKey("highlight_line3", base::Value(changes().highlight_line3()));
- dict.SetKey("highlight_line3", base::Value(changes().highlight_line3()));
+ base::Value(change_flags_.user_approval_required()));
+ dict.SetKey("highlight_title", base::Value(change_flags_.highlight_title()));
+ dict.SetKey("highlight_line1", base::Value(change_flags_.highlight_line1()));
+ dict.SetKey("highlight_line2", base::Value(change_flags_.highlight_line2()));
+ dict.SetKey("highlight_line3", base::Value(change_flags_.highlight_line3()));
+ dict.SetKey("highlight_line3", base::Value(change_flags_.highlight_line3()));
return dict;
}
@@ -186,6 +241,7 @@ bool Details::UpdateFromParameters(const TriggerContext& context) {
proto_.set_animate_placeholders(true);
proto_.set_show_image_placeholder(true);
if (MaybeUpdateFromDetailsParameters(context)) {
+ Update();
return true;
}
@@ -208,10 +264,13 @@ bool Details::UpdateFromParameters(const TriggerContext& context) {
base::Optional<std::string> screening_datetime =
context.GetParameter("MOVIES_SCREENING_DATETIME");
- if (screening_datetime) {
- datetime_ = screening_datetime.value();
+ if (screening_datetime &&
+ ParseDateTimeStringToProto(screening_datetime.value(),
+ proto_.mutable_datetime())) {
is_updated = true;
}
+
+ Update();
return is_updated;
}
@@ -278,8 +337,121 @@ bool Details::MaybeUpdateFromDetailsParameters(const TriggerContext& context) {
return details_updated;
}
+void Details::SetDetailsProto(const DetailsProto& proto) {
+ proto_ = proto;
+ Update();
+}
+
+const std::string Details::title() const {
+ return proto_.title();
+}
+
+int Details::titleMaxLines() const {
+ return title_max_lines_;
+}
+
+const std::string Details::imageUrl() const {
+ return proto_.image_url();
+}
+
+bool Details::imageAllowClickthrough() const {
+ return proto_.image_clickthrough_data().allow_clickthrough();
+}
+
+const std::string Details::imageDescription() const {
+ return proto_.image_clickthrough_data().description();
+}
+
+const std::string Details::imagePositiveText() const {
+ return proto_.image_clickthrough_data().positive_text();
+}
+
+const std::string Details::imageNegativeText() const {
+ return proto_.image_clickthrough_data().negative_text();
+}
+
+const std::string Details::imageClickthroughUrl() const {
+ return proto_.image_clickthrough_data().clickthrough_url();
+}
+
+bool Details::showImagePlaceholder() const {
+ return proto_.show_image_placeholder();
+}
+
+const std::string Details::totalPriceLabel() const {
+ return proto_.total_price_label();
+}
+
+const std::string Details::totalPrice() const {
+ return proto_.total_price();
+}
+
+const std::string Details::descriptionLine1() const {
+ return description_line_1_content_;
+}
+
+const std::string Details::descriptionLine2() const {
+ return proto_.description_line_2();
+}
+
+const std::string Details::descriptionLine3() const {
+ return description_line_3_content_;
+}
+
+const std::string Details::priceAttribution() const {
+ return price_attribution_content_;
+}
+
+bool Details::userApprovalRequired() const {
+ return change_flags_.user_approval_required();
+}
+
+bool Details::highlightTitle() const {
+ return change_flags_.highlight_title();
+}
+
+bool Details::highlightLine1() const {
+ return change_flags_.highlight_line1();
+}
+
+bool Details::highlightLine2() const {
+ return change_flags_.highlight_line2();
+}
+
+bool Details::highlightLine3() const {
+ return change_flags_.highlight_line3();
+}
+
+bool Details::animatePlaceholders() const {
+ return proto_.animate_placeholders();
+}
+
void Details::ClearChanges() {
change_flags_.Clear();
}
+void Details::Update() {
+ auto formatted_datetime = FormatDateTimeProto(proto_.datetime());
+ description_line_1_content_.assign(proto_.description_line_1().empty()
+ ? formatted_datetime
+ : proto_.description_line_1());
+
+ description_line_3_content_.assign(proto_.total_price().empty()
+ ? proto_.description_line_3()
+ : std::string());
+ price_attribution_content_.assign(proto_.total_price().empty()
+ ? std::string()
+ : proto_.description_line_3());
+
+ bool isDescriptionLine1Empty = descriptionLine1().empty();
+ bool isDescriptionLine2Empty = descriptionLine2().empty();
+ if (isDescriptionLine1Empty && isDescriptionLine2Empty) {
+ title_max_lines_ = 3;
+ } else if (isDescriptionLine1Empty || isDescriptionLine2Empty) {
+ title_max_lines_ = 2;
+ } else {
+ title_max_lines_ = 1;
+ }
+}
+
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/details.h b/chromium/components/autofill_assistant/browser/details.h
index ccbdf74875c..fe1e4c6116e 100644
--- a/chromium/components/autofill_assistant/browser/details.h
+++ b/chromium/components/autofill_assistant/browser/details.h
@@ -17,9 +17,8 @@ class TriggerContext;
class Details {
public:
- const DetailsProto& details_proto() const { return proto_; }
- const DetailsChangesProto& changes() const { return change_flags_; }
- const std::string datetime() const { return datetime_; }
+ Details();
+ ~Details();
// Returns a dictionary describing the current execution context, which
// is intended to be serialized as JSON string. The execution context is
@@ -54,27 +53,58 @@ class Details {
ClientMemory* client_memory,
Details* details);
- void SetDetailsProto(const DetailsProto& proto) { proto_ = proto; }
- void SetDetailsChangesProto(const DetailsChangesProto& change_flags) {
- change_flags_ = change_flags;
- }
+ const std::string title() const;
+ int titleMaxLines() const;
+ const std::string imageUrl() const;
+ bool imageAllowClickthrough() const;
+ const std::string imageDescription() const;
+ const std::string imagePositiveText() const;
+ const std::string imageNegativeText() const;
+ const std::string imageClickthroughUrl() const;
+ bool showImagePlaceholder() const;
+ const std::string totalPriceLabel() const;
+ const std::string totalPrice() const;
+ const std::string descriptionLine1() const;
+ const std::string descriptionLine2() const;
+ const std::string descriptionLine3() const;
+ const std::string priceAttribution() const;
+ bool userApprovalRequired() const;
+ bool highlightTitle() const;
+ bool highlightLine1() const;
+ bool highlightLine2() const;
+ bool highlightLine3() const;
+ bool animatePlaceholders() const;
+
// Clears all change flags.
void ClearChanges();
private:
+ void SetDetailsProto(const DetailsProto& proto);
+ void SetDetailsChangesProto(const DetailsChangesProto& change_flags) {
+ change_flags_ = change_flags;
+ }
+
// Tries updating the details using generic detail parameters. Returns true
// if at least one generic detail parameter was found and used.
bool MaybeUpdateFromDetailsParameters(const TriggerContext& context);
+ // Updates fields by taking the current |proto_| values into account.
+ void Update();
+
DetailsProto proto_;
DetailsChangesProto change_flags_;
- // RFC 3339 date-time. Ignore if proto.description_line_1 is set.
- //
- // TODO(crbug.com/806868): parse RFC 3339 date-time on the C++ side and fill
- // proto.description_line_1 with the result instead of carrying a string
- // representation of the datetime.
- std::string datetime_;
+ // Maximum of lines for the title.
+ int title_max_lines_ = 1;
+
+ // Content to be shown in description line 1 in the UI.
+ std::string description_line_1_content_;
+
+ // Content to be shown in description line 3 in the UI.
+ std::string description_line_3_content_;
+
+ // Content to be shown in the price attribution view in the UI.
+ std::string price_attribution_content_;
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/details_unittest.cc b/chromium/components/autofill_assistant/browser/details_unittest.cc
index 8abf364c17d..b23cabe4fc0 100644
--- a/chromium/components/autofill_assistant/browser/details_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/details_unittest.cc
@@ -4,6 +4,7 @@
#include "components/autofill_assistant/browser/details.h"
+#include "base/test/icu_test_util.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/geo/country_names.h"
@@ -19,11 +20,21 @@ namespace {
using ::testing::Eq;
-MATCHER_P(EqualsProto, message, "") {
- std::string expected_serialized, actual_serialized;
- message.SerializeToString(&expected_serialized);
- arg.SerializeToString(&actual_serialized);
- return expected_serialized == actual_serialized;
+void SetDateTimeProto(DateTimeProto* proto,
+ int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) {
+ auto* date = proto->mutable_date();
+ date->set_year(year);
+ date->set_month(month);
+ date->set_day(day);
+ auto* time = proto->mutable_time();
+ time->set_hour(hour);
+ time->set_minute(minute);
+ time->set_second(second);
}
class DetailsTest : public testing::Test {
@@ -50,27 +61,6 @@ class DetailsTest : public testing::Test {
ClientMemory client_memory_;
};
-TEST_F(DetailsTest, DetailsProtoStoredInMemberVariable) {
- Details details;
- DetailsProto proto;
- proto.set_title("title");
-
- details.SetDetailsProto(proto);
- EXPECT_THAT(proto, EqualsProto(details.details_proto()));
-}
-
-TEST_F(DetailsTest, DetailsChangesProto) {
- Details details;
- DetailsChangesProto proto;
- proto.set_user_approval_required(true);
-
- details.SetDetailsChangesProto(proto);
- EXPECT_THAT(proto, EqualsProto(details.changes()));
-
- details.ClearChanges();
- EXPECT_THAT(DetailsChangesProto(), EqualsProto(details.changes()));
-}
-
TEST_F(DetailsTest, UpdateFromParametersEmpty) {
Details details;
// Nothing has to be updated.
@@ -87,13 +77,26 @@ TEST_F(DetailsTest, UpdateFromParametersShowInitialNoUpdate) {
EXPECT_FALSE(details.UpdateFromParameters(*context));
}
+TEST_F(DetailsTest, UpdateFromParametersSetsPlaceholderFlags) {
+ std::map<std::string, std::string> parameters;
+ parameters["DETAILS_SHOW_INITIAL"] = "true";
+
+ auto context = TriggerContext::Create(parameters, "exps");
+
+ Details details;
+ details.UpdateFromParameters(*context);
+
+ EXPECT_TRUE(details.animatePlaceholders());
+ EXPECT_TRUE(details.showImagePlaceholder());
+}
+
TEST_F(DetailsTest, UpdateFromParametersUpdateFromDetails) {
std::map<std::string, std::string> parameters;
parameters["DETAILS_SHOW_INITIAL"] = "true";
parameters["DETAILS_TITLE"] = "title";
parameters["DETAILS_DESCRIPTION_LINE_1"] = "line1";
parameters["DETAILS_DESCRIPTION_LINE_2"] = "line2";
- parameters["DETAILS_DESCRIPTION_LINE_3"] = "line3";
+ parameters["DETAILS_DESCRIPTION_LINE_3"] = "Est. total";
parameters["DETAILS_IMAGE_URL"] = "image";
parameters["DETAILS_IMAGE_CLICKTHROUGH_URL"] = "clickthrough";
parameters["DETAILS_TOTAL_PRICE_LABEL"] = "total";
@@ -104,47 +107,39 @@ TEST_F(DetailsTest, UpdateFromParametersUpdateFromDetails) {
Details details;
EXPECT_TRUE(details.UpdateFromParameters(*context));
- DetailsProto expected;
- expected.set_animate_placeholders(true);
- expected.set_show_image_placeholder(true);
-
- expected.set_title("title");
- expected.set_description_line_1("line1");
- expected.set_description_line_2("line2");
- expected.set_description_line_3("line3");
- expected.set_image_url("image");
- auto* data = expected.mutable_image_clickthrough_data();
- data->set_allow_clickthrough(true);
- data->set_clickthrough_url("clickthrough");
- expected.set_total_price_label("total");
- expected.set_total_price("12");
-
- EXPECT_THAT(details.details_proto(), EqualsProto(expected));
+ EXPECT_TRUE(details.animatePlaceholders());
+ EXPECT_THAT(details.title(), Eq("title"));
+ EXPECT_THAT(details.descriptionLine1(), Eq("line1"));
+ EXPECT_THAT(details.descriptionLine2(), Eq("line2"));
+ EXPECT_THAT(details.priceAttribution(), Eq("Est. total"));
+ EXPECT_THAT(details.imageUrl(),
+ Eq("image")); // Overwrites show_image_placeholder
+ EXPECT_TRUE(details.imageAllowClickthrough());
+ EXPECT_THAT(details.imageClickthroughUrl(), Eq("clickthrough"));
+ EXPECT_THAT(details.totalPriceLabel(), Eq("total"));
+ EXPECT_THAT(details.totalPrice(), Eq("12"));
}
TEST_F(DetailsTest, UpdateFromParametersBackwardsCompatibility) {
+ base::test::ScopedRestoreICUDefaultLocale restore_locale;
+ base::i18n::SetICUDefaultLocale("en_US");
+
std::map<std::string, std::string> parameters;
parameters["MOVIES_MOVIE_NAME"] = "movie_name";
parameters["MOVIES_THEATER_NAME"] = "movie_theater";
- parameters["MOVIES_SCREENING_DATETIME"] = "datetime";
+ parameters["MOVIES_SCREENING_DATETIME"] = "2019-09-26T16:40:02";
auto context = TriggerContext::Create(parameters, "exps");
Details details;
EXPECT_TRUE(details.UpdateFromParameters(*context));
- DetailsProto expected;
- expected.set_animate_placeholders(true);
- expected.set_show_image_placeholder(true);
-
- expected.set_title("movie_name");
- expected.set_description_line_2("movie_theater");
-
- EXPECT_THAT(details.datetime(), "datetime");
- EXPECT_THAT(details.details_proto().title(), "movie_name");
- EXPECT_THAT(details.details_proto().description_line_2(), "movie_theater");
-
- EXPECT_THAT(details.details_proto(), EqualsProto(expected));
+ EXPECT_TRUE(details.animatePlaceholders());
+ EXPECT_TRUE(details.showImagePlaceholder());
+ EXPECT_THAT(details.title(), Eq("movie_name"));
+ EXPECT_THAT(details.descriptionLine2(), Eq("movie_theater"));
+ EXPECT_THAT(details.descriptionLine1(),
+ Eq("4:40 PM \xE2\x80\xA2 Thu, Sep 26"));
}
TEST_F(DetailsTest, UpdateFromProtoNoDetails) {
@@ -152,16 +147,16 @@ TEST_F(DetailsTest, UpdateFromProtoNoDetails) {
EXPECT_FALSE(Details::UpdateFromProto(ShowDetailsProto(), &details));
}
-TEST_F(DetailsTest, UpdateFromProto) {
+TEST_F(DetailsTest, UpdateFromProtoBackwardsCompatibility) {
ShowDetailsProto proto;
proto.mutable_details()->set_title("title");
- proto.mutable_change_flags()->set_user_approval_required(true);
+ proto.mutable_details()->set_description("description");
Details details;
EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
- EXPECT_EQ(details.details_proto().title(), "title");
- EXPECT_TRUE(details.changes().user_approval_required());
+ EXPECT_THAT(details.title(), Eq("title"));
+ EXPECT_THAT(details.descriptionLine2(), Eq("description"));
}
TEST_F(DetailsTest, UpdateFromContactDetailsNoAddressInMemory) {
@@ -178,11 +173,10 @@ TEST_F(DetailsTest, UpdateFromContactDetails) {
EXPECT_TRUE(
Details::UpdateFromContactDetails(proto, &client_memory_, &details));
- const auto& result = details.details_proto();
- EXPECT_THAT(result.title(),
+ EXPECT_THAT(details.title(),
Eq(l10n_util::GetStringUTF8(IDS_PAYMENTS_CONTACT_DETAILS_LABEL)));
- EXPECT_THAT(result.description_line_1(), Eq("Charles Hardin Holley"));
- EXPECT_THAT(result.description_line_2(), Eq("\xE2\x98\xBA@gmail.com"));
+ EXPECT_THAT(details.descriptionLine1(), Eq("Charles Hardin Holley"));
+ EXPECT_THAT(details.descriptionLine2(), Eq("\xE2\x98\xBA@gmail.com"));
}
TEST_F(DetailsTest, UpdateFromShippingAddressNoAddressInMemory) {
@@ -199,12 +193,11 @@ TEST_F(DetailsTest, UpdateFromShippingAddress) {
EXPECT_TRUE(
Details::UpdateFromShippingAddress(proto, &client_memory_, &details));
- const auto& result = details.details_proto();
EXPECT_THAT(
- result.title(),
+ details.title(),
Eq(l10n_util::GetStringUTF8(IDS_PAYMENTS_SHIPPING_ADDRESS_LABEL)));
- EXPECT_THAT(result.description_line_1(), Eq("Charles Hardin Holley"));
- EXPECT_THAT(result.description_line_2(),
+ EXPECT_THAT(details.descriptionLine1(), Eq("Charles Hardin Holley"));
+ EXPECT_THAT(details.descriptionLine2(),
Eq("123 Apple St.\nunit 6 79401 Lubbock US"));
}
@@ -232,13 +225,197 @@ TEST_F(DetailsTest, UpdateFromCreditCard) {
EXPECT_TRUE(
Details::UpdateFromSelectedCreditCard(proto, &client_memory_, &details));
- const auto& result = details.details_proto();
EXPECT_THAT(
- result.title(),
+ details.title(),
Eq(l10n_util::GetStringUTF8(IDS_PAYMENTS_METHOD_OF_PAYMENT_LABEL)));
// The credit card string contains 4 non-ascii dots, we just check that it
// does contain something.
- EXPECT_FALSE(result.description_line_1().empty());
+ EXPECT_FALSE(details.descriptionLine1().empty());
+}
+
+TEST_F(DetailsTest, GetTitleMaxLines) {
+ Details details;
+
+ ShowDetailsProto proto_no_description;
+ proto_no_description.mutable_details()->set_title("title");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_no_description, &details));
+ EXPECT_THAT(details.titleMaxLines(), Eq(3));
+
+ ShowDetailsProto proto_description1;
+ proto_description1.mutable_details()->set_title("title");
+ proto_description1.mutable_details()->set_description_line_1("line 1");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_description1, &details));
+ EXPECT_THAT(details.titleMaxLines(), Eq(2));
+
+ ShowDetailsProto proto_description2;
+ proto_description2.mutable_details()->set_title("title");
+ proto_description2.mutable_details()->set_description_line_2("line 2");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_description2, &details));
+ EXPECT_THAT(details.titleMaxLines(), Eq(2));
+
+ ShowDetailsProto proto_description1_date;
+ proto_description1_date.mutable_details()->set_title("title");
+ SetDateTimeProto(
+ proto_description1_date.mutable_details()->mutable_datetime(), 2019, 9,
+ 26, 16, 40, 2);
+ EXPECT_TRUE(Details::UpdateFromProto(proto_description1_date, &details));
+ EXPECT_THAT(details.titleMaxLines(), Eq(2));
+
+ ShowDetailsProto proto_both_descriptions;
+ proto_both_descriptions.mutable_details()->set_title("title");
+ proto_both_descriptions.mutable_details()->set_description_line_1("line 1");
+ proto_both_descriptions.mutable_details()->set_description_line_2("line 2");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_both_descriptions, &details));
+ EXPECT_THAT(details.titleMaxLines(), Eq(1));
+}
+
+TEST_F(DetailsTest, GetDescriptionLine1) {
+ base::test::ScopedRestoreICUDefaultLocale restore_locale;
+
+ Details details;
+
+ ShowDetailsProto proto_description;
+ proto_description.mutable_details()->set_description_line_1("line 1");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_description, &details));
+ EXPECT_THAT(details.descriptionLine1(), Eq("line 1"));
+
+ base::i18n::SetICUDefaultLocale("en_US");
+ ShowDetailsProto proto_date;
+ SetDateTimeProto(proto_date.mutable_details()->mutable_datetime(), 2019, 9,
+ 25, 15, 16, 0);
+ EXPECT_TRUE(Details::UpdateFromProto(proto_date, &details));
+ EXPECT_THAT(details.descriptionLine1(),
+ Eq("3:16 PM \xE2\x80\xA2 Wed, Sep 25"));
+
+ base::i18n::SetICUDefaultLocale("de_DE");
+ ShowDetailsProto proto_date_de;
+ SetDateTimeProto(proto_date.mutable_details()->mutable_datetime(), 2019, 9,
+ 25, 15, 16, 0);
+ EXPECT_TRUE(Details::UpdateFromProto(proto_date, &details));
+ EXPECT_THAT(details.descriptionLine1(),
+ Eq("3:16 PM \xE2\x80\xA2 Mi., 25. Sept."));
+
+ ShowDetailsProto proto_empty;
+ proto_empty.mutable_details()->set_title("title");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_empty, &details));
+ EXPECT_THAT(details.descriptionLine1(), Eq(""));
+}
+
+TEST_F(DetailsTest, GetDescriptionLine2) {
+ Details details;
+
+ ShowDetailsProto proto_description;
+ proto_description.mutable_details()->set_title("title");
+ proto_description.mutable_details()->set_description_line_2("line 2");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_description, &details));
+ EXPECT_THAT(details.descriptionLine2(), Eq("line 2"));
+}
+
+TEST_F(DetailsTest, GetDescriptionLine3) {
+ Details details;
+
+ ShowDetailsProto proto_no_price;
+ proto_no_price.mutable_details()->set_title("title");
+ proto_no_price.mutable_details()->set_description_line_3("line 3");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_no_price, &details));
+ EXPECT_THAT(details.descriptionLine3(), Eq("line 3"));
+
+ ShowDetailsProto proto_with_price;
+ proto_with_price.mutable_details()->set_title("title");
+ proto_with_price.mutable_details()->set_description_line_3("Est. total");
+ proto_with_price.mutable_details()->set_total_price("$2.50");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_with_price, &details));
+ EXPECT_THAT(details.descriptionLine3(), Eq(""));
+}
+
+TEST_F(DetailsTest, GetPriceAttribution) {
+ Details details;
+
+ ShowDetailsProto proto_no_price;
+ proto_no_price.mutable_details()->set_title("title");
+ proto_no_price.mutable_details()->set_description_line_3("line 3");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_no_price, &details));
+ EXPECT_THAT(details.priceAttribution(), Eq(""));
+
+ ShowDetailsProto proto_with_price;
+ proto_with_price.mutable_details()->set_title("title");
+ proto_with_price.mutable_details()->set_description_line_3("Est. total");
+ proto_with_price.mutable_details()->set_total_price("$2.50");
+ EXPECT_TRUE(Details::UpdateFromProto(proto_with_price, &details));
+ EXPECT_THAT(details.priceAttribution(), Eq("Est. total"));
+}
+
+TEST_F(DetailsTest, GetTitle) {
+ Details details;
+ ShowDetailsProto proto;
+ proto.mutable_details()->set_title("title");
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_THAT(details.title(), Eq("title"));
+}
+
+TEST_F(DetailsTest, GetImageUrl) {
+ Details details;
+ ShowDetailsProto proto;
+ proto.mutable_details()->set_image_url("url");
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_THAT(details.imageUrl(), Eq("url"));
+}
+
+TEST_F(DetailsTest, GetClickthroughData) {
+ Details details;
+ ShowDetailsProto proto;
+ auto* clitkthrough_data =
+ proto.mutable_details()->mutable_image_clickthrough_data();
+ clitkthrough_data->set_allow_clickthrough(true);
+ clitkthrough_data->set_description("description");
+ clitkthrough_data->set_positive_text("positive");
+ clitkthrough_data->set_negative_text("negative");
+ proto.mutable_details()
+ ->mutable_image_clickthrough_data()
+ ->set_clickthrough_url("url");
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_TRUE(details.imageAllowClickthrough());
+ EXPECT_THAT(details.imageDescription(), Eq("description"));
+ EXPECT_THAT(details.imagePositiveText(), Eq("positive"));
+ EXPECT_THAT(details.imageNegativeText(), Eq("negative"));
+ EXPECT_THAT(details.imageClickthroughUrl(), Eq("url"));
+}
+
+TEST_F(DetailsTest, GetPlaceholderFlags) {
+ Details details;
+ ShowDetailsProto proto;
+ proto.mutable_details()->set_show_image_placeholder(true);
+ proto.mutable_details()->set_animate_placeholders(true);
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_TRUE(details.showImagePlaceholder());
+ EXPECT_TRUE(details.animatePlaceholders());
+}
+
+TEST_F(DetailsTest, GetTotalPrice) {
+ Details details;
+ ShowDetailsProto proto;
+ proto.mutable_details()->set_total_price_label("Total");
+ proto.mutable_details()->set_total_price("$2.50");
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_THAT(details.totalPriceLabel(), Eq("Total"));
+ EXPECT_THAT(details.totalPrice(), Eq("$2.50"));
+}
+
+TEST_F(DetailsTest, GetHighlightFlags) {
+ Details details;
+ ShowDetailsProto proto;
+ proto.mutable_details()->set_title("title");
+ proto.mutable_change_flags()->set_user_approval_required(true);
+ proto.mutable_change_flags()->set_highlight_title(true);
+ proto.mutable_change_flags()->set_highlight_line1(true);
+ proto.mutable_change_flags()->set_highlight_line2(true);
+ proto.mutable_change_flags()->set_highlight_line3(true);
+ EXPECT_TRUE(Details::UpdateFromProto(proto, &details));
+ EXPECT_TRUE(details.userApprovalRequired());
+ EXPECT_TRUE(details.highlightTitle());
+ EXPECT_TRUE(details.highlightLine1());
+ EXPECT_TRUE(details.highlightLine2());
+ EXPECT_TRUE(details.highlightLine3());
}
} // namespace
diff --git a/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template b/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
index ffa1cde75b6..309c0878a77 100644
--- a/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
+++ b/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_cc.template
@@ -48,8 +48,9 @@ void Domain::RegisterEventHandlersIfNeeded() {
{% set method_name = command.name | sanitize_literal | to_title_case %}
void {{class_name}}::{{method_name}}(
std::unique_ptr<{{method_name}}Params> params,
+ const std::string& optional_node_frame_id,
base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback) {
- dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
}
{# Generate convenience methods that take the required parameters directly. #}
{% if not "parameters" in command %}{% continue %}{% endif %}
@@ -65,6 +66,7 @@ void {{class_name}}::{{method_name}}({##}
{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
{% endfor %}
{% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+ const std::string& optional_node_frame_id,
{% if command.get("returns", []) -%}
base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback{##}
{% else -%}
@@ -81,16 +83,16 @@ void {{class_name}}::{{method_name}}({##}
.Build();
{# Send the message. #}
{% if command.get("returns", []) -%}
- dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, base::BindOnce(&Domain::Handle{{method_name}}Response, std::move(callback)));
{% else %}
- dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, std::move(callback));
{% endif %}
}
{# If the command has no return value, generate a convenience method that #}
{# accepts a base::OnceClosure together with the parameters object. #}
{% if not command.get("returns", []) %}
-void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceClosure callback) {
- dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
+void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceClosure callback) {
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), optional_node_frame_id, std::move(callback));
}
{% endif %}
{% endfor %}
diff --git a/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template b/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
index 785d4cd7152..0d0afca14f2 100644
--- a/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
+++ b/chromium/components/autofill_assistant/browser/devtools/devtools_api/domain_h.template
@@ -23,7 +23,7 @@
{% if command.description %}
// {{ command.description.replace('\n', '\n // ') }}
{% endif %}
- void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>());
+ void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>());
{# Generate convenience methods that take the required parameters directly. #}
{# Don't generate these for experimental commands. #}
{% if "parameters" in command and not command.experimental %}
@@ -36,6 +36,7 @@
{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
{% endfor %}
{% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+ const std::string& optional_node_frame_id,
{% if command.get("returns", []) -%}
base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)> callback = base::OnceCallback<void(const MessageDispatcher::ReplyStatus&, std::unique_ptr<{{method_name}}Result>)>(){##}
{% else -%}
@@ -44,7 +45,7 @@
{# If the command has no return value, generate a convenience method that #}
{# accepts a base::Closure together with the parameters object. #}
{% if not command.get("returns", []) %}
- void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::OnceClosure callback);
+ void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, const std::string& optional_node_frame_id, base::OnceClosure callback);
{% endif %}
{% endif %}
{% endmacro %}
diff --git a/chromium/components/autofill_assistant/browser/devtools/devtools_client.cc b/chromium/components/autofill_assistant/browser/devtools/devtools_client.cc
index 39911cdb7a0..964d07bb5a4 100644
--- a/chromium/components/autofill_assistant/browser/devtools/devtools_client.cc
+++ b/chromium/components/autofill_assistant/browser/devtools/devtools_client.cc
@@ -26,14 +26,18 @@ DevtoolsClient::DevtoolsClient(
dom_domain_(this),
runtime_domain_(this),
network_domain_(this),
+ target_domain_(this),
renderer_crashed_(false),
- next_message_id_(0) {
+ next_message_id_(0),
+ frame_tracker_(this) {
browser_main_thread_ =
base::CreateSingleThreadTaskRunner({content::BrowserThread::UI});
agent_host_->AttachClient(this);
+ frame_tracker_.Start();
}
DevtoolsClient::~DevtoolsClient() {
+ frame_tracker_.Stop();
agent_host_->DetachClient(this);
}
@@ -53,27 +57,43 @@ network::Domain* DevtoolsClient::GetNetwork() {
return &network_domain_;
}
+target::ExperimentalDomain* DevtoolsClient::GetTarget() {
+ return &target_domain_;
+}
+
void DevtoolsClient::SendMessage(
const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceCallback<void(const ReplyStatus&, const base::Value&)> callback) {
- SendMessageWithParams(method, std::move(params), std::move(callback));
+ SendMessageWithParams(method, std::move(params), optional_node_frame_id,
+ std::move(callback));
}
void DevtoolsClient::SendMessage(const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceClosure callback) {
- SendMessageWithParams(method, std::move(params), std::move(callback));
+ SendMessageWithParams(method, std::move(params), optional_node_frame_id,
+ std::move(callback));
}
template <typename CallbackType>
-void DevtoolsClient::SendMessageWithParams(const char* method,
- std::unique_ptr<base::Value> params,
- CallbackType callback) {
+void DevtoolsClient::SendMessageWithParams(
+ const char* method,
+ std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
+ CallbackType callback) {
base::DictionaryValue message;
message.SetString("method", method);
message.Set("params", std::move(params));
+ std::string optional_session_id =
+ GetSessionIdForFrame(optional_node_frame_id);
+ if (!optional_session_id.empty()) {
+ message.SetString("sessionId", optional_session_id);
+ }
+
if (renderer_crashed_)
return;
int id = next_message_id_;
@@ -95,6 +115,11 @@ void DevtoolsClient::RegisterEventHandler(
event_handlers_[method] = std::move(callback);
}
+void DevtoolsClient::UnregisterEventHandler(const char* method) {
+ DCHECK(event_handlers_.find(method) != event_handlers_.end());
+ event_handlers_.erase(method);
+}
+
void DevtoolsClient::DispatchProtocolMessage(
content::DevToolsAgentHost* agent_host,
const std::string& json_message) {
@@ -254,6 +279,11 @@ void DevtoolsClient::AgentHostClosed(content::DevToolsAgentHost* agent_host) {
renderer_crashed_ = true;
}
+std::string DevtoolsClient::GetSessionIdForFrame(
+ const std::string& frame_id) const {
+ return frame_tracker_.GetSessionIdForFrame(frame_id);
+}
+
DevtoolsClient::Callback::Callback() = default;
DevtoolsClient::Callback::Callback(Callback&& other) = default;
@@ -270,4 +300,114 @@ DevtoolsClient::Callback::~Callback() = default;
DevtoolsClient::Callback& DevtoolsClient::Callback::operator=(
Callback&& other) = default;
+DevtoolsClient::FrameTracker::FrameTracker(DevtoolsClient* client)
+ : client_(client) {}
+
+DevtoolsClient::FrameTracker::~FrameTracker() = default;
+
+void DevtoolsClient::FrameTracker::Start() {
+ client_->RegisterEventHandler(
+ "Target.attachedToTarget",
+ base::BindRepeating(&DevtoolsClient::FrameTracker::OnAttachedToTarget,
+ weak_ptr_factory_.GetWeakPtr()));
+ client_->RegisterEventHandler(
+ "Target.detachedFromTarget",
+ base::BindRepeating(&DevtoolsClient::FrameTracker::OnDetachedFromTarget,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ started_ = true;
+
+ // Start auto attaching so that we can keep track of what session got started
+ // for what target. We use flatten = true to cover the entire frame tree.
+ client_->GetTarget()->SetAutoAttach(
+ target::SetAutoAttachParams::Builder()
+ .SetAutoAttach(true)
+ .SetWaitForDebuggerOnStart(false)
+ .SetFlatten(true)
+ .Build(),
+ /* node_frame_id= */ "",
+ base::BindOnce(&DevtoolsClient::FrameTracker::OnSetAutoAttach,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DevtoolsClient::FrameTracker::Stop() {
+ if (!started_) {
+ return;
+ }
+
+ client_->UnregisterEventHandler("Target.attachedToTarget");
+ client_->UnregisterEventHandler("Target.detachedFromTarget");
+
+ started_ = false;
+}
+
+void DevtoolsClient::FrameTracker::OnSetAutoAttach(
+ const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<target::SetAutoAttachResult> result) {
+ // This is not used since result doesn't contain anything useful. The real
+ // action is happening in the On(Attached|Detached) functions.
+ DCHECK(result);
+}
+
+std::string DevtoolsClient::FrameTracker::GetSessionIdForFrame(
+ std::string frame_id) const {
+ if (frame_id.empty()) {
+ return std::string();
+ }
+
+ auto it = sessions_map_.find(frame_id);
+ if (it != sessions_map_.end()) {
+ return it->second;
+ }
+ DVLOG(3) << "No session id for frame_id: " << frame_id;
+ return std::string();
+}
+
+std::string DevtoolsClient::FrameTracker::FindTargetId(
+ const base::Value& value) {
+ const base::Value* target_info = value.FindKey("targetInfo");
+ if (!target_info) {
+ DVLOG(3) << "No target_info found in " << value;
+ return std::string();
+ }
+ const std::string* target_id = target_info->FindStringKey("targetId");
+ if (!target_id) {
+ DVLOG(3) << "No target_id found in " << *target_info;
+ return std::string();
+ }
+
+ return *target_id;
+}
+
+std::string DevtoolsClient::FrameTracker::FindSessionId(
+ const base::Value& value) {
+ const std::string* session_id = value.FindStringKey("sessionId");
+ if (!session_id) {
+ DVLOG(3) << "No session_id found in " << value;
+ return std::string();
+ }
+
+ return *session_id;
+}
+
+void DevtoolsClient::FrameTracker::OnAttachedToTarget(
+ const base::Value& value) {
+ std::string session_id = FindSessionId(value);
+ std::string target_id = FindTargetId(value);
+
+ if (!session_id.empty() && !target_id.empty()) {
+ sessions_map_[target_id] = session_id;
+ }
+}
+
+void DevtoolsClient::FrameTracker::OnDetachedFromTarget(
+ const base::Value& value) {
+ std::string target_id = FindTargetId(value);
+
+ auto it = sessions_map_.find(target_id);
+ if (it != sessions_map_.end()) {
+ sessions_map_.erase(it);
+ }
+}
+
} // namespace autofill_assistant.
diff --git a/chromium/components/autofill_assistant/browser/devtools/devtools_client.h b/chromium/components/autofill_assistant/browser/devtools/devtools_client.h
index 4b86b9ecc65..2ec99084bc3 100644
--- a/chromium/components/autofill_assistant/browser/devtools/devtools_client.h
+++ b/chromium/components/autofill_assistant/browser/devtools/devtools_client.h
@@ -23,6 +23,7 @@
#include "components/autofill_assistant/browser/devtools/devtools/domains/input.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/network.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/runtime.h"
+#include "components/autofill_assistant/browser/devtools/devtools/domains/target.h"
#include "components/autofill_assistant/browser/devtools/message_dispatcher.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_agent_host_client.h"
@@ -39,19 +40,23 @@ class DevtoolsClient : public MessageDispatcher,
dom::Domain* GetDOM();
runtime::Domain* GetRuntime();
network::Domain* GetNetwork();
+ target::ExperimentalDomain* GetTarget();
// MessageDispatcher implementation:
void SendMessage(
const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceCallback<void(const ReplyStatus&, const base::Value&)> callback)
override;
void SendMessage(const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceClosure callback) override;
void RegisterEventHandler(
const char* method,
base::RepeatingCallback<void(const base::Value&)> callback) override;
+ void UnregisterEventHandler(const char* method) override;
// content::DevToolsAgentHostClient overrides:
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
@@ -76,9 +81,52 @@ class DevtoolsClient : public MessageDispatcher,
callback_with_result;
};
+ // Manages a map to retrieve a session id from a given frame id. Registers
+ // itself to client events.
+ class FrameTracker {
+ public:
+ // Register the event handlers and start tracking new targets. |client|
+ // must outlive this frame tracker.
+ FrameTracker(DevtoolsClient* client);
+ ~FrameTracker();
+
+ void Start();
+ void Stop();
+
+ // Returns empty string if there is no session for the given |frame_id|.
+ std::string GetSessionIdForFrame(std::string frame_id) const;
+
+ private:
+ void OnSetAutoAttach(const DevtoolsClient::ReplyStatus& reply_status,
+ std::unique_ptr<target::SetAutoAttachResult> result);
+ void OnAttachedToTarget(const base::Value& value);
+ void OnDetachedFromTarget(const base::Value& value);
+
+ // Save find of targetInfo.targetId in the given value. Returns
+ // empty string if nothing is found.
+ std::string FindTargetId(const base::Value& value);
+
+ // Find sessionId in the given value. Returns empty string if nothing is
+ // found.
+ std::string FindSessionId(const base::Value& value);
+
+ DevtoolsClient* client_;
+ bool started_ = false;
+
+ // Holds the mappings from frame id to session id.
+ std::unordered_map<std::string, std::string> sessions_map_;
+
+ base::WeakPtrFactory<FrameTracker> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(FrameTracker);
+ };
+
+ // If the frame is known to devtools, return the session id for it.
+ std::string GetSessionIdForFrame(const std::string& frame_id) const;
+
template <typename CallbackType>
void SendMessageWithParams(const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
CallbackType callback);
bool DispatchMessageReply(std::unique_ptr<base::Value> owning_message,
const base::DictionaryValue& message_dict);
@@ -105,10 +153,12 @@ class DevtoolsClient : public MessageDispatcher,
dom::ExperimentalDomain dom_domain_;
runtime::ExperimentalDomain runtime_domain_;
network::ExperimentalDomain network_domain_;
+ target::ExperimentalDomain target_domain_;
std::unordered_map<int, Callback> pending_messages_;
EventHandlerMap event_handlers_;
bool renderer_crashed_;
int next_message_id_;
+ FrameTracker frame_tracker_;
base::WeakPtrFactory<DevtoolsClient> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(DevtoolsClient);
diff --git a/chromium/components/autofill_assistant/browser/devtools/message_dispatcher.h b/chromium/components/autofill_assistant/browser/devtools/message_dispatcher.h
index a5171389127..71126c85202 100644
--- a/chromium/components/autofill_assistant/browser/devtools/message_dispatcher.h
+++ b/chromium/components/autofill_assistant/browser/devtools/message_dispatcher.h
@@ -38,15 +38,18 @@ class MessageDispatcher {
virtual void SendMessage(
const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceCallback<void(const ReplyStatus&, const base::Value&)>
callback) = 0;
virtual void SendMessage(const char* method,
std::unique_ptr<base::Value> params,
+ const std::string& optional_node_frame_id,
base::OnceClosure callback) = 0;
virtual void RegisterEventHandler(
const char* method,
base::RepeatingCallback<void(const base::Value&)> callback) = 0;
+ virtual void UnregisterEventHandler(const char* method) = 0;
protected:
virtual ~MessageDispatcher() {}
diff --git a/chromium/components/autofill_assistant/browser/element_precondition.cc b/chromium/components/autofill_assistant/browser/element_precondition.cc
index 45efbfd773c..59f0986d3a6 100644
--- a/chromium/components/autofill_assistant/browser/element_precondition.cc
+++ b/chromium/components/autofill_assistant/browser/element_precondition.cc
@@ -42,7 +42,7 @@ void ElementPrecondition::Check(BatchElementChecker* batch_checks,
callback_ = std::move(callback);
for (const auto& selector : elements_exist_) {
- base::OnceCallback<void(bool)> callback =
+ base::OnceCallback<void(const ClientStatus&)> callback =
base::BindOnce(&ElementPrecondition::OnCheckElementExists,
weak_ptr_factory_.GetWeakPtr());
batch_checks->AddElementCheck(selector, std::move(callback));
@@ -57,14 +57,15 @@ void ElementPrecondition::Check(BatchElementChecker* batch_checks,
}
}
-void ElementPrecondition::OnCheckElementExists(bool exists) {
- ReportCheckResult(exists);
+void ElementPrecondition::OnCheckElementExists(
+ const ClientStatus& element_status) {
+ ReportCheckResult(element_status.ok());
}
void ElementPrecondition::OnGetFieldValue(int index,
- bool exists,
+ const ClientStatus& element_status,
const std::string& value) {
- if (!exists) {
+ if (!element_status.ok()) {
ReportCheckResult(false);
return;
}
diff --git a/chromium/components/autofill_assistant/browser/element_precondition.h b/chromium/components/autofill_assistant/browser/element_precondition.h
index 9e7497f2791..edfce0d6719 100644
--- a/chromium/components/autofill_assistant/browser/element_precondition.h
+++ b/chromium/components/autofill_assistant/browser/element_precondition.h
@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
@@ -37,8 +38,10 @@ class ElementPrecondition {
bool empty() { return elements_exist_.empty() && form_value_match_.empty(); }
private:
- void OnCheckElementExists(bool exists);
- void OnGetFieldValue(int index, bool exists, const std::string& value);
+ void OnCheckElementExists(const ClientStatus& element_status);
+ void OnGetFieldValue(int index,
+ const ClientStatus& element_status,
+ const std::string& value);
void ReportCheckResult(bool success);
std::vector<Selector> elements_exist_;
diff --git a/chromium/components/autofill_assistant/browser/element_precondition_unittest.cc b/chromium/components/autofill_assistant/browser/element_precondition_unittest.cc
index eb00f23c9c3..542528b7700 100644
--- a/chromium/components/autofill_assistant/browser/element_precondition_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/element_precondition_unittest.cc
@@ -28,20 +28,20 @@ class ElementPreconditionTest : public testing::Test {
public:
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"exists"})), _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"empty"})), _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"does_not_exist"})), _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
ON_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"exists"})), _))
- .WillByDefault(RunOnceCallback<1>(true, "foo"));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "foo"));
ON_CALL(mock_web_controller_,
OnGetFieldValue(Eq(Selector({"does_not_exist"})), _))
- .WillByDefault(RunOnceCallback<1>(false, ""));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus(), ""));
ON_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"empty"})), _))
- .WillByDefault(RunOnceCallback<1>(true, ""));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus(), ""));
}
protected:
diff --git a/chromium/components/autofill_assistant/browser/mock_controller_observer.h b/chromium/components/autofill_assistant/browser/mock_controller_observer.h
index 151bdc1b461..995f6629cbc 100644
--- a/chromium/components/autofill_assistant/browser/mock_controller_observer.h
+++ b/chromium/components/autofill_assistant/browser/mock_controller_observer.h
@@ -13,6 +13,7 @@
#include "components/autofill_assistant/browser/controller_observer.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/script.h"
+#include "components/autofill_assistant/browser/ui_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
@@ -24,6 +25,7 @@ class MockControllerObserver : public ControllerObserver {
MOCK_METHOD1(OnStatusMessageChanged, void(const std::string& message));
MOCK_METHOD1(OnBubbleMessageChanged, void(const std::string& message));
+ MOCK_METHOD0(CloseCustomTab, void());
MOCK_METHOD1(OnStateChanged, void(AutofillAssistantState));
MOCK_METHOD1(OnUserActionsChanged,
void(const std::vector<UserAction>& user_actions));
@@ -43,9 +45,10 @@ class MockControllerObserver : public ControllerObserver {
MOCK_METHOD1(OnViewportModeChanged, void(ViewportMode mode));
MOCK_METHOD1(OnPeekModeChanged,
void(ConfigureBottomSheetProto::PeekMode peek_mode));
+ MOCK_METHOD1(OnOverlayColorsChanged,
+ void(const UiDelegate::OverlayColors& colors));
MOCK_METHOD1(OnFormChanged, void(const FormProto* form));
-
- // TODO(b/141163294): add missing methods and unit tests.
+ MOCK_METHOD1(OnClientSettingsChanged, void(const ClientSettings& settings));
};
} // namespace autofill_assistant
diff --git a/chromium/components/autofill_assistant/browser/protocol_utils.cc b/chromium/components/autofill_assistant/browser/protocol_utils.cc
index da35b0958c5..fee2055417d 100644
--- a/chromium/components/autofill_assistant/browser/protocol_utils.cc
+++ b/chromium/components/autofill_assistant/browser/protocol_utils.cc
@@ -8,7 +8,6 @@
#include "base/feature_list.h"
#include "base/logging.h"
-#include "components/autofill_assistant/browser/actions/autofill_action.h"
#include "components/autofill_assistant/browser/actions/click_action.h"
#include "components/autofill_assistant/browser/actions/collect_user_data_action.h"
#include "components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h"
@@ -30,6 +29,8 @@
#include "components/autofill_assistant/browser/actions/tell_action.h"
#include "components/autofill_assistant/browser/actions/unsupported_action.h"
#include "components/autofill_assistant/browser/actions/upload_dom_action.h"
+#include "components/autofill_assistant/browser/actions/use_address_action.h"
+#include "components/autofill_assistant/browser/actions/use_credit_card_action.h"
#include "components/autofill_assistant/browser/actions/wait_for_document_action.h"
#include "components/autofill_assistant/browser/actions/wait_for_dom_action.h"
#include "components/autofill_assistant/browser/actions/wait_for_navigation_action.h"
@@ -51,6 +52,9 @@ void FillClientContext(const ClientContextProto& client_context,
if (trigger_context.is_cct()) {
proto->set_is_cct(true);
}
+ if (trigger_context.is_onboarding_shown()) {
+ proto->set_is_onboarding_shown(true);
+ }
if (trigger_context.is_direct_action()) {
proto->set_is_direct_action(true);
}
@@ -209,8 +213,10 @@ bool ProtocolUtils::ParseActions(ActionDelegate* delegate,
break;
}
case ActionProto::ActionInfoCase::kUseAddress:
+ client_action = std::make_unique<UseAddressAction>(delegate, action);
+ break;
case ActionProto::ActionInfoCase::kUseCard: {
- client_action = std::make_unique<AutofillAction>(delegate, action);
+ client_action = std::make_unique<UseCreditCardAction>(delegate, action);
break;
}
case ActionProto::ActionInfoCase::kWaitForDom: {
diff --git a/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc b/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 741be9339d2..9441f611e16 100644
--- a/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -98,6 +98,7 @@ TEST(ProtocolUtilsTest, CreateInitialScriptActionsRequest) {
AssertClientContext(request.client_context());
EXPECT_THAT(request.client_context().experiment_ids(), Eq("1,2,3"));
EXPECT_TRUE(request.client_context().is_cct());
+ EXPECT_FALSE(request.client_context().is_onboarding_shown());
EXPECT_FALSE(request.client_context().is_direct_action());
const InitialScriptActionsRequestProto& initial = request.initial_request();
@@ -112,6 +113,41 @@ TEST(ProtocolUtilsTest, CreateInitialScriptActionsRequest) {
EXPECT_EQ("script_payload", request.script_payload());
}
+TEST(ProtocolUtilsTest, TestCreateInitialScriptActionsRequestFlags) {
+ std::map<std::string, std::string> parameters;
+
+ ScriptActionRequestProto request;
+
+ // With flags.
+ TriggerContextImpl trigger_context_flags(parameters, std::string());
+ trigger_context_flags.SetCCT(true);
+ trigger_context_flags.SetOnboardingShown(true);
+ trigger_context_flags.SetDirectAction(true);
+
+ EXPECT_TRUE(
+ request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
+ "script_path", GURL("http://example.com/"), trigger_context_flags,
+ "global_payload", "script_payload", CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_TRUE(request.client_context().is_cct());
+ EXPECT_TRUE(request.client_context().is_onboarding_shown());
+ EXPECT_TRUE(request.client_context().is_direct_action());
+
+ // Without flags.
+ TriggerContextImpl trigger_context_no_flags(parameters, std::string());
+
+ EXPECT_TRUE(
+ request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
+ "script_path", GURL("http://example.com/"), trigger_context_no_flags,
+ "global_payload", "script_payload", CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_FALSE(request.client_context().is_cct());
+ EXPECT_FALSE(request.client_context().is_onboarding_shown());
+ EXPECT_FALSE(request.client_context().is_direct_action());
+}
+
TEST(ProtocolUtilsTest, CreateNextScriptActionsRequest) {
std::map<std::string, std::string> parameters;
parameters["a"] = "b";
@@ -131,6 +167,44 @@ TEST(ProtocolUtilsTest, CreateNextScriptActionsRequest) {
EXPECT_EQ(1, request.next_request().processed_actions().size());
}
+TEST(ProtocolUtilsTest, TestCreateNextScriptActionsRequestFlags) {
+ std::map<std::string, std::string> parameters;
+
+ std::vector<ProcessedActionProto> processed_actions;
+ processed_actions.emplace_back(ProcessedActionProto());
+
+ ScriptActionRequestProto request;
+
+ // With flags.
+ TriggerContextImpl trigger_context_flags(parameters, std::string());
+ trigger_context_flags.SetCCT(true);
+ trigger_context_flags.SetOnboardingShown(true);
+ trigger_context_flags.SetDirectAction(true);
+
+ EXPECT_TRUE(
+ request.ParseFromString(ProtocolUtils::CreateNextScriptActionsRequest(
+ trigger_context_flags, "global_payload", "script_payload",
+ processed_actions, CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_TRUE(request.client_context().is_cct());
+ EXPECT_TRUE(request.client_context().is_onboarding_shown());
+ EXPECT_TRUE(request.client_context().is_direct_action());
+
+ // Without flags.
+ TriggerContextImpl trigger_context_no_flags(parameters, std::string());
+
+ EXPECT_TRUE(
+ request.ParseFromString(ProtocolUtils::CreateNextScriptActionsRequest(
+ trigger_context_no_flags, "global_payload", "script_payload",
+ processed_actions, CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_FALSE(request.client_context().is_cct());
+ EXPECT_FALSE(request.client_context().is_onboarding_shown());
+ EXPECT_FALSE(request.client_context().is_direct_action());
+}
+
TEST(ProtocolUtilsTest, CreateGetScriptsRequest) {
std::map<std::string, std::string> parameters;
parameters["a"] = "b";
@@ -146,6 +220,7 @@ TEST(ProtocolUtilsTest, CreateGetScriptsRequest) {
AssertClientContext(request.client_context());
EXPECT_THAT(request.client_context().experiment_ids(), Eq("1,2,3"));
EXPECT_FALSE(request.client_context().is_cct());
+ EXPECT_FALSE(request.client_context().is_onboarding_shown());
EXPECT_TRUE(request.client_context().is_direct_action());
EXPECT_EQ("http://example.com/", request.url());
@@ -156,6 +231,38 @@ TEST(ProtocolUtilsTest, CreateGetScriptsRequest) {
EXPECT_EQ("d", request.script_parameters(1).value());
}
+TEST(ProtocolUtilsTest, TestCreateGetScriptsRequestFlags) {
+ std::map<std::string, std::string> parameters;
+ SupportsScriptRequestProto request;
+
+ // With flags.
+ TriggerContextImpl trigger_context_flags(parameters, std::string());
+ trigger_context_flags.SetCCT(true);
+ trigger_context_flags.SetOnboardingShown(true);
+ trigger_context_flags.SetDirectAction(true);
+
+ EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
+ GURL("http://example.com/"), trigger_context_flags,
+ CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_TRUE(request.client_context().is_cct());
+ EXPECT_TRUE(request.client_context().is_onboarding_shown());
+ EXPECT_TRUE(request.client_context().is_direct_action());
+
+ // Without flags.
+ TriggerContextImpl trigger_context_no_flags(parameters, std::string());
+
+ EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
+ GURL("http://example.com/"), trigger_context_no_flags,
+ CreateClientContextProto())));
+
+ AssertClientContext(request.client_context());
+ EXPECT_FALSE(request.client_context().is_cct());
+ EXPECT_FALSE(request.client_context().is_onboarding_shown());
+ EXPECT_FALSE(request.client_context().is_direct_action());
+}
+
TEST(ProtocolUtilsTest, AddScriptIgnoreInvalid) {
SupportedScriptProto script_proto;
std::vector<std::unique_ptr<Script>> scripts;
diff --git a/chromium/components/autofill_assistant/browser/retry_timer.cc b/chromium/components/autofill_assistant/browser/retry_timer.cc
index 4ded92bbd45..5ee87740abf 100644
--- a/chromium/components/autofill_assistant/browser/retry_timer.cc
+++ b/chromium/components/autofill_assistant/browser/retry_timer.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant {
@@ -14,8 +15,9 @@ RetryTimer::~RetryTimer() = default;
void RetryTimer::Start(
base::TimeDelta max_wait_time,
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)> task,
- base::OnceCallback<void(bool)> on_done) {
+ base::RepeatingCallback<void(base::OnceCallback<void(const ClientStatus&)>)>
+ task,
+ base::OnceCallback<void(const ClientStatus&)> on_done) {
Reset();
task_ = std::move(task);
on_done_ = std::move(on_done);
@@ -47,15 +49,15 @@ void RetryTimer::RunTask() {
weak_ptr_factory_.GetWeakPtr(), task_id_));
}
-void RetryTimer::OnTaskDone(int64_t task_id, bool success) {
+void RetryTimer::OnTaskDone(int64_t task_id, const ClientStatus& status) {
if (task_id != task_id_) // Ignore callbacks from cancelled tasks
return;
remaining_attempts_--;
- if (success || remaining_attempts_ <= 0) {
+ if (status.ok() || remaining_attempts_ <= 0) {
CHECK_GE(remaining_attempts_, 0);
task_.Reset(); // release any resources held by the callback
- std::move(on_done_).Run(success);
+ std::move(on_done_).Run(status);
// Don't do anything after calling on_done_, as it could have deleted this.
return;
}
diff --git a/chromium/components/autofill_assistant/browser/retry_timer.h b/chromium/components/autofill_assistant/browser/retry_timer.h
index af5876d6a47..40b964040b5 100644
--- a/chromium/components/autofill_assistant/browser/retry_timer.h
+++ b/chromium/components/autofill_assistant/browser/retry_timer.h
@@ -12,6 +12,7 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant {
@@ -39,8 +40,9 @@ class RetryTimer {
// If |max_wait_time| is 0 or lower than the retry period, the task is
// executed exactly once.
void Start(base::TimeDelta max_wait_time,
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)> task,
- base::OnceCallback<void(bool)> on_done);
+ base::RepeatingCallback<
+ void(base::OnceCallback<void(const ClientStatus&)>)> task,
+ base::OnceCallback<void(const ClientStatus&)> on_done);
// Cancels any pending tasks or timer. Any |on_done| callbacks passed to Start
// is released without being called.
@@ -54,13 +56,14 @@ class RetryTimer {
private:
void Reset();
void RunTask();
- void OnTaskDone(int64_t task_id_, bool success);
+ void OnTaskDone(int64_t task_id_, const ClientStatus& status);
const base::TimeDelta period_;
int64_t remaining_attempts_ = 1;
int64_t task_id_ = 0;
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)> task_;
- base::OnceCallback<void(bool)> on_done_;
+ base::RepeatingCallback<void(base::OnceCallback<void(const ClientStatus&)>)>
+ task_;
+ base::OnceCallback<void(const ClientStatus&)> on_done_;
std::unique_ptr<base::OneShotTimer> timer_;
base::WeakPtrFactory<RetryTimer> weak_ptr_factory_{this};
diff --git a/chromium/components/autofill_assistant/browser/retry_timer_unittest.cc b/chromium/components/autofill_assistant/browser/retry_timer_unittest.cc
index 1bad81484dc..727f3765c67 100644
--- a/chromium/components/autofill_assistant/browser/retry_timer_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/retry_timer_unittest.cc
@@ -20,6 +20,10 @@ namespace autofill_assistant {
namespace {
+MATCHER_P(EqualsStatus, status, "") {
+ return arg.proto_status() == status.proto_status();
+}
+
class RetryTimerTest : public testing::Test {
protected:
RetryTimerTest()
@@ -29,37 +33,38 @@ class RetryTimerTest : public testing::Test {
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
}
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)>
+ base::RepeatingCallback<void(base::OnceCallback<void(const ClientStatus&)>)>
AlwaysFailsCallback() {
return base::BindRepeating(&RetryTimerTest::AlwaysFails,
base::Unretained(this));
}
- void AlwaysFails(base::OnceCallback<void(bool)> callback) {
+ void AlwaysFails(base::OnceCallback<void(const ClientStatus&)> callback) {
try_count_++;
- std::move(callback).Run(false);
+ std::move(callback).Run(ClientStatus());
}
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)>
+ base::RepeatingCallback<void(base::OnceCallback<void(const ClientStatus&)>)>
SucceedsOnceCallback(int succeds_at) {
return base::BindRepeating(&RetryTimerTest::SucceedsOnce,
base::Unretained(this), succeds_at);
}
- void SucceedsOnce(int succeeds_at, base::OnceCallback<void(bool)> callback) {
+ void SucceedsOnce(int succeeds_at,
+ base::OnceCallback<void(const ClientStatus&)> callback) {
EXPECT_GE(succeeds_at, try_count_);
bool success = succeeds_at == try_count_;
try_count_++;
- std::move(callback).Run(success);
+ std::move(callback).Run(success ? OkClientStatus() : ClientStatus());
}
- base::RepeatingCallback<void(base::OnceCallback<void(bool)>)>
+ base::RepeatingCallback<void(base::OnceCallback<void(const ClientStatus&)>)>
CaptureCallback() {
return base::BindRepeating(&RetryTimerTest::Capture,
base::Unretained(this));
}
- void Capture(base::OnceCallback<void(bool)> callback) {
+ void Capture(base::OnceCallback<void(const ClientStatus&)> callback) {
try_count_++;
captured_callback_ = std::move(callback);
}
@@ -69,13 +74,14 @@ class RetryTimerTest : public testing::Test {
base::test::TaskEnvironment task_environment_;
int try_count_ = 0;
- base::OnceCallback<void(bool)> captured_callback_;
- base::MockCallback<base::OnceCallback<void(bool)>> done_callback_;
+ base::OnceCallback<void(const ClientStatus&)> captured_callback_;
+ base::MockCallback<base::OnceCallback<void(const ClientStatus&)>>
+ done_callback_;
};
TEST_F(RetryTimerTest, TryOnceAndSucceed) {
RetryTimer retry_timer(base::TimeDelta::FromSeconds(1));
- EXPECT_CALL(done_callback_, Run(true));
+ EXPECT_CALL(done_callback_, Run(EqualsStatus(OkClientStatus())));
retry_timer.Start(base::TimeDelta::FromSeconds(10), SucceedsOnceCallback(0),
done_callback_.Get());
EXPECT_EQ(1, try_count_);
@@ -83,7 +89,7 @@ TEST_F(RetryTimerTest, TryOnceAndSucceed) {
TEST_F(RetryTimerTest, TryOnceAndFail) {
RetryTimer retry_timer(base::TimeDelta::FromSeconds(1));
- EXPECT_CALL(done_callback_, Run(false));
+ EXPECT_CALL(done_callback_, Run(EqualsStatus(ClientStatus())));
retry_timer.Start(base::TimeDelta::FromSeconds(0), AlwaysFailsCallback(),
done_callback_.Get());
EXPECT_EQ(1, try_count_);
@@ -96,7 +102,7 @@ TEST_F(RetryTimerTest, TryMultipleTimesAndSucceed) {
EXPECT_EQ(1, try_count_);
FastForwardOneSecond();
EXPECT_EQ(2, try_count_);
- EXPECT_CALL(done_callback_, Run(true));
+ EXPECT_CALL(done_callback_, Run(EqualsStatus(OkClientStatus())));
FastForwardOneSecond();
EXPECT_EQ(3, try_count_);
}
@@ -108,7 +114,7 @@ TEST_F(RetryTimerTest, TryMultipleTimesAndFail) {
EXPECT_EQ(1, try_count_);
FastForwardOneSecond();
EXPECT_EQ(2, try_count_);
- EXPECT_CALL(done_callback_, Run(false));
+ EXPECT_CALL(done_callback_, Run(EqualsStatus(ClientStatus())));
FastForwardOneSecond();
EXPECT_EQ(3, try_count_);
}
@@ -130,7 +136,7 @@ TEST_F(RetryTimerTest, CancelWithPendingCallbacks) {
done_callback_.Get());
ASSERT_TRUE(captured_callback_);
retry_timer.Cancel();
- std::move(captured_callback_).Run(true); // Should do nothing
+ std::move(captured_callback_).Run(OkClientStatus()); // Should do nothing
}
TEST_F(RetryTimerTest, GiveUpWhenLeavingScope) {
@@ -152,7 +158,7 @@ TEST_F(RetryTimerTest, GiveUpWhenLeavingScopeWithPendingCallback) {
done_callback_.Get());
ASSERT_TRUE(captured_callback_);
}
- std::move(captured_callback_).Run(true); // Should do nothing
+ std::move(captured_callback_).Run(OkClientStatus()); // Should do nothing
}
TEST_F(RetryTimerTest, RestartOverridesFirstCall) {
@@ -161,11 +167,12 @@ TEST_F(RetryTimerTest, RestartOverridesFirstCall) {
RetryTimer retry_timer(base::TimeDelta::FromSeconds(1));
retry_timer.Start(base::TimeDelta::FromSeconds(1), AlwaysFailsCallback(),
done_callback_.Get());
- base::MockCallback<base::OnceCallback<void(bool)>> done_callback2;
+ base::MockCallback<base::OnceCallback<void(const ClientStatus&)>>
+ done_callback2;
retry_timer.Start(base::TimeDelta::FromSeconds(1), AlwaysFailsCallback(),
done_callback2.Get());
EXPECT_EQ(2, try_count_);
- EXPECT_CALL(done_callback2, Run(false));
+ EXPECT_CALL(done_callback2, Run(EqualsStatus(ClientStatus())));
FastForwardOneSecond();
EXPECT_EQ(3, try_count_);
}
@@ -178,13 +185,14 @@ TEST_F(RetryTimerTest, RestartOverridesFirstCallWithPendingTask) {
done_callback_.Get());
ASSERT_TRUE(captured_callback_);
- base::MockCallback<base::OnceCallback<void(bool)>> done_callback2;
+ base::MockCallback<base::OnceCallback<void(const ClientStatus&)>>
+ done_callback2;
retry_timer.Start(base::TimeDelta::FromSeconds(1), AlwaysFailsCallback(),
done_callback2.Get());
- std::move(captured_callback_).Run(true); // Should do nothing
+ std::move(captured_callback_).Run(OkClientStatus()); // Should do nothing
- EXPECT_CALL(done_callback2, Run(false));
+ EXPECT_CALL(done_callback2, Run(EqualsStatus(ClientStatus())));
FastForwardOneSecond();
EXPECT_EQ(3, try_count_);
}
@@ -197,7 +205,7 @@ TEST_F(RetryTimerTest, Running) {
done_callback_.Get());
EXPECT_TRUE(retry_timer.running());
- EXPECT_CALL(done_callback_, Run(true));
+ EXPECT_CALL(done_callback_, Run(EqualsStatus(OkClientStatus())));
FastForwardOneSecond();
EXPECT_FALSE(retry_timer.running());
}
diff --git a/chromium/components/autofill_assistant/browser/script_executor.cc b/chromium/components/autofill_assistant/browser/script_executor.cc
index acef62b3243..8b7ae9c8065 100644
--- a/chromium/components/autofill_assistant/browser/script_executor.cc
+++ b/chromium/components/autofill_assistant/browser/script_executor.cc
@@ -154,7 +154,7 @@ void ScriptExecutor::RunElementChecks(BatchElementChecker* checker) {
void ScriptExecutor::ShortWaitForElement(
const Selector& selector,
- base::OnceCallback<void(bool)> callback) {
+ base::OnceCallback<void(const ClientStatus&)> callback) {
current_action_data_.wait_for_dom = std::make_unique<WaitForDomOperation>(
this, delegate_, delegate_->GetSettings().short_wait_for_element_deadline,
/* allow_interrupt= */ false,
@@ -169,9 +169,9 @@ void ScriptExecutor::WaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
+ base::OnceCallback<void(const ClientStatus&)>)>
check_elements,
- base::OnceCallback<void(ProcessedActionStatusProto)> callback) {
+ base::OnceCallback<void(const ClientStatus&)> callback) {
current_action_data_.wait_for_dom = std::make_unique<WaitForDomOperation>(
this, delegate_, max_wait_time, allow_interrupt, check_elements,
base::BindOnce(&ScriptExecutor::OnWaitForElementVisibleWithInterrupts,
@@ -373,7 +373,8 @@ void ScriptExecutor::SetProgressVisible(bool visible) {
void ScriptExecutor::GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) {
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback) {
delegate_->GetWebController()->GetFieldValue(selector, std::move(callback));
}
@@ -710,38 +711,37 @@ void ScriptExecutor::OnProcessedAction(
void ScriptExecutor::CheckElementMatches(
const Selector& selector,
BatchElementChecker* checker,
- base::OnceCallback<void(bool)> callback) {
+ base::OnceCallback<void(const ClientStatus&)> callback) {
checker->AddElementCheck(selector, std::move(callback));
}
void ScriptExecutor::OnShortWaitForElement(
- base::OnceCallback<void(bool)> callback,
- bool element_found,
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ const ClientStatus& element_status,
const Result* interrupt_result) {
// Interrupts cannot run, so should never be reported.
DCHECK(!interrupt_result);
- std::move(callback).Run(element_found);
+ std::move(callback).Run(element_status);
}
void ScriptExecutor::OnWaitForElementVisibleWithInterrupts(
- base::OnceCallback<void(ProcessedActionStatusProto)> callback,
- bool element_found,
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ const ClientStatus& element_status,
const Result* interrupt_result) {
if (interrupt_result) {
if (!interrupt_result->success) {
- std::move(callback).Run(INTERRUPT_FAILED);
+ std::move(callback).Run(ClientStatus(INTERRUPT_FAILED));
return;
}
if (interrupt_result->at_end != CONTINUE) {
at_end_ = interrupt_result->at_end;
should_stop_script_ = true;
- std::move(callback).Run(MANUAL_FALLBACK);
+ std::move(callback).Run(ClientStatus(MANUAL_FALLBACK));
return;
}
}
- std::move(callback).Run(element_found ? ACTION_APPLIED
- : ELEMENT_RESOLUTION_FAILED);
+ std::move(callback).Run(element_status);
}
ScriptExecutor::WaitForDomOperation::WaitForDomOperation(
@@ -750,7 +750,7 @@ ScriptExecutor::WaitForDomOperation::WaitForDomOperation(
base::TimeDelta max_wait_time,
bool allow_interrupt,
base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
+ base::OnceCallback<void(const ClientStatus&)>)>
check_elements,
WaitForDomOperation::Callback callback)
: main_script_(main_script),
@@ -822,9 +822,9 @@ void ScriptExecutor::WaitForDomOperation::OnScriptListChanged(
}
void ScriptExecutor::WaitForDomOperation::RunChecks(
- base::OnceCallback<void(bool)> report_attempt_result) {
+ base::OnceCallback<void(const ClientStatus&)> report_attempt_result) {
// Reset state possibly left over from previous runs.
- element_check_result_ = false;
+ element_check_result_ = ClientStatus();
runnable_interrupts_.clear();
batch_element_checker_ = std::make_unique<BatchElementChecker>();
check_elements_.Run(batch_element_checker_.get(),
@@ -860,15 +860,16 @@ void ScriptExecutor::WaitForDomOperation::OnPreconditionCheckDone(
runnable_interrupts_.insert(interrupt);
}
-void ScriptExecutor::WaitForDomOperation::OnElementCheckDone(bool result) {
- element_check_result_ = result;
+void ScriptExecutor::WaitForDomOperation::OnElementCheckDone(
+ const ClientStatus& element_status) {
+ element_check_result_ = element_status;
// Wait for all checks to run before reporting that the element was found to
// the caller, so interrupts have a chance to run.
}
void ScriptExecutor::WaitForDomOperation::OnAllChecksDone(
- base::OnceCallback<void(bool)> report_attempt_result) {
+ base::OnceCallback<void(const ClientStatus&)> report_attempt_result) {
if (!runnable_interrupts_.empty()) {
// We must go through runnable_interrupts_ to make sure priority order is
// respected in case more than one interrupt is ready to run.
@@ -903,7 +904,7 @@ void ScriptExecutor::WaitForDomOperation::OnInterruptDone(
const ScriptExecutor::Result& result) {
interrupt_executor_.reset();
if (!result.success || result.at_end != ScriptExecutor::CONTINUE) {
- RunCallbackWithResult(false, &result);
+ RunCallbackWithResult(ClientStatus(INTERRUPT_FAILED), &result);
return;
}
RestoreStatusMessage();
@@ -914,12 +915,13 @@ void ScriptExecutor::WaitForDomOperation::OnInterruptDone(
Start();
}
-void ScriptExecutor::WaitForDomOperation::RunCallback(bool found) {
- RunCallbackWithResult(found, nullptr);
+void ScriptExecutor::WaitForDomOperation::RunCallback(
+ const ClientStatus& element_status) {
+ RunCallbackWithResult(element_status, nullptr);
}
void ScriptExecutor::WaitForDomOperation::RunCallbackWithResult(
- bool check_result,
+ const ClientStatus& element_status,
const ScriptExecutor::Result* result) {
// stop element checking if one is still in progress
batch_element_checker_.reset();
@@ -928,7 +930,7 @@ void ScriptExecutor::WaitForDomOperation::RunCallbackWithResult(
return;
RestorePreInterruptScroll();
- std::move(callback_).Run(check_result, result);
+ std::move(callback_).Run(element_status, result);
}
void ScriptExecutor::WaitForDomOperation::SavePreInterruptState() {
diff --git a/chromium/components/autofill_assistant/browser/script_executor.h b/chromium/components/autofill_assistant/browser/script_executor.h
index aa79feb0621..4313941bb1f 100644
--- a/chromium/components/autofill_assistant/browser/script_executor.h
+++ b/chromium/components/autofill_assistant/browser/script_executor.h
@@ -101,15 +101,16 @@ class ScriptExecutor : public ActionDelegate,
// Override ActionDelegate:
void RunElementChecks(BatchElementChecker* checker) override;
- void ShortWaitForElement(const Selector& selector,
- base::OnceCallback<void(bool)> callback) override;
+ void ShortWaitForElement(
+ const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&)> callback) override;
void WaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
- check_elements,
- base::OnceCallback<void(ProcessedActionStatusProto)> callback) override;
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements,
+ base::OnceCallback<void(const ClientStatus&)> callback) override;
void SetStatusMessage(const std::string& message) override;
std::string GetStatusMessage() override;
void SetBubbleMessage(const std::string& message) override;
@@ -148,7 +149,8 @@ class ScriptExecutor : public ActionDelegate,
const ElementAreaProto& touchable_element_area) override;
void GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) override;
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback) override;
void SetFieldValue(
const Selector& selector,
const std::string& value,
@@ -219,8 +221,8 @@ class ScriptExecutor : public ActionDelegate,
//
// If the given result is non-null, it should be forwarded as the result of
// the main script.
- using Callback =
- base::OnceCallback<void(bool, const ScriptExecutor::Result*)>;
+ using Callback = base::OnceCallback<void(const ClientStatus&,
+ const ScriptExecutor::Result*)>;
// |main_script_| must not be null and outlive this instance.
WaitForDomOperation(
@@ -228,9 +230,9 @@ class ScriptExecutor : public ActionDelegate,
ScriptExecutorDelegate* delegate,
base::TimeDelta max_wait_time,
bool allow_interrupt,
- base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
- check_elements,
+ base::RepeatingCallback<
+ void(BatchElementChecker*,
+ base::OnceCallback<void(const ClientStatus&)>)> check_elements,
WaitForDomOperation::Callback callback);
~WaitForDomOperation() override;
@@ -251,15 +253,17 @@ class ScriptExecutor : public ActionDelegate,
void OnScriptListChanged(
std::vector<std::unique_ptr<Script>> scripts) override;
- void RunChecks(base::OnceCallback<void(bool)> report_attempt_result);
+ void RunChecks(
+ base::OnceCallback<void(const ClientStatus&)> report_attempt_result);
void OnPreconditionCheckDone(const Script* interrupt,
bool precondition_match);
- void OnElementCheckDone(bool found);
- void OnAllChecksDone(base::OnceCallback<void(bool)> report_attempt_result);
+ void OnElementCheckDone(const ClientStatus&);
+ void OnAllChecksDone(
+ base::OnceCallback<void(const ClientStatus&)> report_attempt_result);
void RunInterrupt(const Script* interrupt);
void OnInterruptDone(const ScriptExecutor::Result& result);
- void RunCallback(bool found);
- void RunCallbackWithResult(bool found,
+ void RunCallback(const ClientStatus& element_status);
+ void RunCallbackWithResult(const ClientStatus& element_status,
const ScriptExecutor::Result* result);
// Saves the current state and sets save_pre_interrupt_state_.
@@ -277,13 +281,13 @@ class ScriptExecutor : public ActionDelegate,
const base::TimeDelta max_wait_time_;
const bool allow_interrupt_;
base::RepeatingCallback<void(BatchElementChecker*,
- base::OnceCallback<void(bool)>)>
+ base::OnceCallback<void(const ClientStatus&)>)>
check_elements_;
WaitForDomOperation::Callback callback_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
std::set<const Script*> runnable_interrupts_;
- bool element_check_result_ = false;
+ ClientStatus element_check_result_;
// An empty vector of interrupts that can be passed to interrupt_executor_
// and outlives it. Interrupts must not run interrupts.
@@ -321,15 +325,17 @@ class ScriptExecutor : public ActionDelegate,
void GetNextActions();
void OnProcessedAction(base::TimeTicks start_time,
std::unique_ptr<ProcessedActionProto> action);
- void CheckElementMatches(const Selector& selector,
- BatchElementChecker* checker,
- base::OnceCallback<void(bool)> callback);
- void OnShortWaitForElement(base::OnceCallback<void(bool)> callback,
- bool element_found,
- const Result* interrupt_result);
+ void CheckElementMatches(
+ const Selector& selector,
+ BatchElementChecker* checker,
+ base::OnceCallback<void(const ClientStatus&)> callback);
+ void OnShortWaitForElement(
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ const ClientStatus& element_status,
+ const Result* interrupt_result);
void OnWaitForElementVisibleWithInterrupts(
- base::OnceCallback<void(ProcessedActionStatusProto)> callback,
- bool element_found,
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ const ClientStatus& element_status,
const Result* interrupt_result);
void OnGetUserData(
base::OnceCallback<void(std::unique_ptr<UserData>)> callback,
diff --git a/chromium/components/autofill_assistant/browser/script_executor_unittest.cc b/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
index f6d73e7576b..8fdd457c6da 100644
--- a/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -69,7 +69,7 @@ class ScriptExecutorTest : public testing::Test,
.WillByDefault(RunOnceCallback<1>(ClientStatus(OTHER_ACTION_STATUS)));
ON_CALL(mock_web_controller_, OnElementCheck(_, _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_web_controller_, OnFocusElement(_, _, _))
.WillByDefault(RunOnceCallback<2>(OkClientStatus()));
}
@@ -524,12 +524,12 @@ TEST_F(ScriptExecutorTest, WaitForDomWaitUntil) {
// element is found, and the action succeeds.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
executor_->Run(executor_callback_.Get());
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(executor_callback_, Run(_));
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
@@ -553,12 +553,12 @@ TEST_F(ScriptExecutorTest, WaitForDomWaitWhile) {
// disappears, and the action succeeds.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
executor_->Run(executor_callback_.Get());
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(executor_callback_, Run(_));
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
@@ -724,10 +724,10 @@ TEST_F(ScriptExecutorTest, DoNotRunInterruptIfPreconditionsDontMatch) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"interrupt_trigger"})), _))
- .WillRepeatedly(RunOnceCallback<1>(false));
+ .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _))
.WillRepeatedly(RunOnceCallback<4>(true, ""));
@@ -1002,7 +1002,7 @@ TEST_F(ScriptExecutorTest, PauseWaitForDomWhileNavigating) {
// First check does not find the element, wait for dom waits 1s.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
executor_->Run(executor_callback_.Get());
// Navigation starts while WaitForDom is waiting. The action doesn't fail,
@@ -1016,7 +1016,7 @@ TEST_F(ScriptExecutorTest, PauseWaitForDomWhileNavigating) {
// The end of navigation un-pauses WaitForDom.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
EXPECT_CALL(executor_callback_, Run(_));
delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ false);
@@ -1156,14 +1156,14 @@ TEST_F(ScriptExecutorTest, ReportNavigationEnd) {
// fails.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ false);
// Checking for the element succeeds on the second try. Waiting avoids
// depending on the order at which the listeners are called.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
ASSERT_THAT(processed_actions_capture, SizeIs(1));
@@ -1187,7 +1187,7 @@ TEST_F(ScriptExecutorTest, ReportUnexpectedNavigationStart) {
// As the element doesn't exist, WaitForDom returns and waits for 1s.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(executor_callback_, Run(_));
executor_->Run(executor_callback_.Get());
@@ -1196,7 +1196,7 @@ TEST_F(ScriptExecutorTest, ReportUnexpectedNavigationStart) {
// Navigation end forces a re-check, which succeeds
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ false);
ASSERT_THAT(processed_actions_capture, SizeIs(1));
@@ -1221,7 +1221,7 @@ TEST_F(ScriptExecutorTest, ReportExpectedNavigationStart) {
// As the element doesn't exist, WaitForDom returns and waits for 1s.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
EXPECT_CALL(executor_callback_, Run(_));
executor_->Run(executor_callback_.Get());
@@ -1230,7 +1230,7 @@ TEST_F(ScriptExecutorTest, ReportExpectedNavigationStart) {
// Navigation end forces a re-check, which succeeds
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"element"})), _))
- .WillRepeatedly(RunOnceCallback<1>(true));
+ .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ false);
ASSERT_THAT(processed_actions_capture, SizeIs(2));
diff --git a/chromium/components/autofill_assistant/browser/script_precondition_unittest.cc b/chromium/components/autofill_assistant/browser/script_precondition_unittest.cc
index d4057a1bff8..68e1a77af81 100644
--- a/chromium/components/autofill_assistant/browser/script_precondition_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/script_precondition_unittest.cc
@@ -58,10 +58,10 @@ class ScriptPreconditionTest : public testing::Test {
public:
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"exists"})), _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"does_not_exist"})), _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
SetUrl("http://www.example.com/path");
@@ -177,7 +177,7 @@ TEST_F(ScriptPreconditionTest, BadPathPattern) {
TEST_F(ScriptPreconditionTest, IgnoreEmptyElementsExist) {
EXPECT_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"exists"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
ScriptPreconditionProto proto;
proto.add_elements_exist()->add_selectors("exists");
diff --git a/chromium/components/autofill_assistant/browser/script_tracker_unittest.cc b/chromium/components/autofill_assistant/browser/script_tracker_unittest.cc
index 131774ab00f..29d84a93fb2 100644
--- a/chromium/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -36,10 +36,10 @@ class ScriptTrackerTest : public testing::Test, public ScriptTracker::Listener {
delegate_.SetCurrentURL(GURL("http://www.example.com/"));
ON_CALL(mock_web_controller_, OnElementCheck(Eq(Selector({"exists"})), _))
- .WillByDefault(RunOnceCallback<1>(true));
+ .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
ON_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"does_not_exist"})), _))
- .WillByDefault(RunOnceCallback<1>(false));
+ .WillByDefault(RunOnceCallback<1>(ClientStatus()));
// Scripts run, but have no actions.
ON_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
@@ -153,7 +153,6 @@ TEST_F(ScriptTrackerTest, SomeRunnableScripts) {
}
TEST_F(ScriptTrackerTest, DoNotCheckInterruptWithNoName) {
-
// The interrupt's preconditions would all be met, but it won't be reported
// since it doesn't have a name.
auto* no_name = AddScript("", "path1", "exists");
@@ -264,7 +263,7 @@ TEST_F(ScriptTrackerTest, CheckScriptsAgainAfterScriptEnd) {
TEST_F(ScriptTrackerTest, CheckScriptsAfterDOMChange) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"maybe_exists"})), _))
- .WillOnce(RunOnceCallback<1>(false));
+ .WillOnce(RunOnceCallback<1>(ClientStatus()));
AddScript("script name", "script path", "maybe_exists");
SetAndCheckScripts();
@@ -272,10 +271,10 @@ TEST_F(ScriptTrackerTest, CheckScriptsAfterDOMChange) {
// No scripts are runnable.
EXPECT_THAT(runnable_scripts(), IsEmpty());
- // DOM has changed; OnElementExists now returns true.
+ // DOM has changed; OnElementExists now returns truthy.
EXPECT_CALL(mock_web_controller_,
OnElementCheck(Eq(Selector({"maybe_exists"})), _))
- .WillOnce(RunOnceCallback<1>(true));
+ .WillOnce(RunOnceCallback<1>(OkClientStatus()));
tracker_.CheckScripts();
// The script can now run
diff --git a/chromium/components/autofill_assistant/browser/service.proto b/chromium/components/autofill_assistant/browser/service.proto
index e4be415d685..4e0ea9275fa 100644
--- a/chromium/components/autofill_assistant/browser/service.proto
+++ b/chromium/components/autofill_assistant/browser/service.proto
@@ -42,6 +42,9 @@ message ClientContextProto {
// Assistant.
optional bool is_cct = 8;
+ // True if the onboarding screen was shown to the user.
+ optional bool is_onboarding_shown = 10;
+
// True if the script was triggered by a direct action.
optional bool is_direct_action = 9;
}
@@ -73,7 +76,39 @@ message SupportsScriptResponseProto {
optional ClientSettingsProto client_settings = 3;
}
+// Represents a dimension, e.g., width or height.
+message ClientDimensionProto {
+ oneof size {
+ int32 dpi = 1;
+ // Factor to multiply with the client's total width.
+ float width_factor = 2;
+ // Factor to multiply with the client's total height.
+ float height_factor = 3;
+ }
+}
+
+// Overlay image to be drawn on top of full overlays.
+message OverlayImageProto {
+ // The image to display. If set, |image_size| is mandatory.
+ optional string image_url = 1;
+ // The size of the image to display.
+ optional ClientDimensionProto image_size = 2;
+ // The margin between the top of the page (anchor) and the image.
+ optional ClientDimensionProto image_top_margin = 3;
+ // The margin between the bottom of the image and the baseline of the text.
+ optional ClientDimensionProto image_bottom_margin = 4;
+ // The text to display beneath the image. If set, |text_color| and |text_size|
+ // are mandatory.
+ optional string text = 5;
+ // Supported formats: #RRGGBB or #AARRGGBB.
+ optional string text_color = 6;
+ // The size of the text to display.
+ optional ClientDimensionProto text_size = 7;
+}
+
message ClientSettingsProto {
+ reserved 10, 11;
+
// Time between two periodic script precondition checks.
optional int32 periodic_script_check_interval_ms = 1;
@@ -108,14 +143,6 @@ message ClientSettingsProto {
// ready.
optional int32 document_ready_check_count = 9;
- // Whether graceful shutdown should be enabled. If false, the UI stays
- // up until it's dismissed.
- optional bool enable_graceful_shutdown = 10;
-
- // How long to wait before shutting down during graceful shutdown. If 0
- // shutdown happens immediately.
- optional int32 graceful_shutdown_delay_ms = 11;
-
// How much time to give users to tap undo when they tap a cancel button.
optional int32 cancel_delay_ms = 12;
@@ -130,6 +157,9 @@ message ClientSettingsProto {
// How much time to give users to tap undo when after |tap_count| unexpected
// taps where
optional int32 tap_shutdown_delay_ms = 15;
+
+ // Optional image drawn on top of overlays.
+ optional OverlayImageProto overlay_image = 16;
}
message ScriptTimeoutError {
@@ -440,6 +470,8 @@ message ActionProto {
// script finishes with this action. It has no effect if there is any other
// action sent to the client after this one. Default is false.
optional bool clean_contextual_ui = 33;
+
+ reserved 47;
}
// Result of |CollectUserDataProto| to be sent to the server.
@@ -458,6 +490,10 @@ message CollectUserDataResultProto {
optional int32 terms_link = 5;
// The payload of the chosen login option.
optional bytes login_payload = 6;
+ // The start of the date/time range, if requested.
+ optional DateTimeProto date_time_start = 7;
+ // The end of the date/time range, if requested.
+ optional DateTimeProto date_time_end = 8;
}
message ProcessedActionProto {
@@ -513,8 +549,6 @@ message ProcessedActionStatusDetailsProto {
optional ProcessedActionStatusProto original_status = 2;
// More information included for autofill related errors.
- //
- // For now this is only filled for PRECONDITION_FAILED errors.
optional AutofillErrorInfoProto autofill_error_info = 3;
}
@@ -583,8 +617,15 @@ message AutofillErrorInfoProto {
// Name of the address key requested in the list of keys in
// |client_memory_address_key_names|.
optional string address_key_requested = 2;
+
+ // Whether the client memory at |address_key_requested| pointed to null.
+ optional bool address_pointee_was_null = 3;
+
+ // Error status of the Chrome autofill attempt.
+ optional ProcessedActionStatusProto autofill_error_status = 4;
}
+// Next: 22
enum ProcessedActionStatusProto {
UNKNOWN_ACTION_STATUS = 0;
@@ -684,6 +725,9 @@ enum ProcessedActionStatusProto {
// The requested autofill info (e.g., Chrome password manager login) was not
// available. It might have been recently deleted.
AUTOFILL_INFO_NOT_AVAILABLE = 21;
+
+ // An unexpected error occurred during element resolution.
+ FRAME_HOST_NOT_FOUND = 22;
}
// The pseudo type values come from
@@ -901,6 +945,9 @@ message UseCreditCardProto {
CREDIT_CARD_EXP_MONTH = 2;
CREDIT_CARD_EXP_2_DIGIT_YEAR = 3;
CREDIT_CARD_EXP_4_DIGIT_YEAR = 4;
+ CREDIT_CARD_CARD_HOLDER_NAME = 5;
+ CREDIT_CARD_NUMBER = 6;
+ CREDIT_CARD_EXP_MM_YY = 7;
}
optional CardField card_field = 1;
@@ -1159,6 +1206,14 @@ message ContactDetailsProto {
optional bool request_payer_phone = 4;
}
+// A generic read-only popup message.
+message InfoPopupProto {
+ // The title of the popup window.
+ optional string title = 1;
+ // The text of the popup window.
+ optional string text = 2;
+}
+
message LoginDetailsProto {
// A custom login option which will be handled by the backend, e.g.,
// 'Guest checkout' or 'Log in with Google'.
@@ -1171,6 +1226,13 @@ message LoginDetailsProto {
message LoginOptionPasswordManagerProto {}
message LoginOptionProto {
+ // If set, an info icon will be shown that displays a popup when tapped.
+ optional InfoPopupProto info_popup = 6;
+
+ // The optional sublabel to display beneath the label.
+ optional string sublabel = 7;
+ optional string sublabel_accessibility_hint = 8;
+
// If the option was chosen, this payload will be returned to the server.
optional bytes payload = 1;
@@ -1192,8 +1254,63 @@ message LoginDetailsProto {
repeated LoginOptionProto login_options = 2;
}
+message DateTimeRangeProto {
+ // The start value of the date/time range.
+ optional DateTimeProto start = 1;
+ // The end value of the date/time range.
+ optional DateTimeProto end = 2;
+ // The minimum allowed value of the date/time range.
+ optional DateTimeProto min = 3;
+ // The maximum allowed value of the date/time range.
+ optional DateTimeProto max = 4;
+ // The label of the start date/time value (e.g., 'Pick-up').
+ optional string start_label = 5;
+ // The label of the end date/time value (e.g., 'Return').
+ optional string end_label = 6;
+}
+
+// A section showing a simple text message.
+message StaticTextSectionProto {
+ // The text to display. Can contain markup tags like <b>.
+ optional string text = 1;
+}
+
+// A single text input.
+message TextInputProto {
+ optional string hint = 1;
+ enum InputType {
+ UNDEFINED = 0;
+ // Regular text, no special formatting rules.
+ INPUT_TEXT = 1;
+ // An alphanumeric input, e.g. postal code or discount code.
+ INPUT_ALPHANUMERIC = 2;
+ }
+ optional InputType input_type = 2;
+
+ // The client memory key to store the result in.
+ optional string client_memory_key = 3;
+
+ // The initial value of the text input.
+ optional string value = 4;
+}
+
+// A section containing one or multiple text inputs.
+message TextInputSectionProto {
+ repeated TextInputProto input_fields = 1;
+}
+
+message UserFormSectionProto {
+ optional string title = 1;
+
+ oneof section {
+ StaticTextSectionProto static_text_section = 2;
+ TextInputSectionProto text_input_section = 3;
+ }
+}
+
// Asks to provide the data used by UseAddressAction and
// UseCreditCardAction.
+// Next: 22
message CollectUserDataProto {
enum TermsAndConditionsState {
// No choice has been made yet.
@@ -1236,6 +1353,12 @@ message CollectUserDataProto {
// that will finish this action and return the clicked link in the action
// result.
optional string accept_terms_and_conditions_text = 13;
+ // Message that indicates that the user wants to review the terms and
+ // conditions of a 3rd party's domain, e.g., 'example.com'.
+ optional string terms_require_review_text = 20;
+ // Privacy notice telling users that autofill assistant will send personal
+ // data to a third party’s website.
+ optional string thirdparty_privacy_notice_text = 21;
// Optionally allows confiriming through the given direct actions.
optional DirectActionProto confirm_direct_action = 10;
// Additional actions available to the user. This can be used for instance to
@@ -1250,6 +1373,12 @@ message CollectUserDataProto {
optional string billing_postal_code_missing_text = 15;
// The login details that should be gathered.
optional LoginDetailsProto login_details = 16;
+ // The date/time range that should be gathered.
+ optional DateTimeRangeProto date_time_range = 17;
+ // An optional list of additional sections, which is above all other sections.
+ repeated UserFormSectionProto additional_prepended_sections = 18;
+ // An optional list of additional sections, which is below all other sections.
+ repeated UserFormSectionProto additional_appended_sections = 19;
}
// Resets Autofill Assistant: clears any state and server payload.
@@ -1399,6 +1528,8 @@ message SetFormFieldValueProto {
// Use the password from the Chrome password manager login previously
// selected in a CollectUserDataAction.
bool use_password = 5;
+ // Use the value stored at the specified memory location.
+ string client_memory_key = 6;
}
}
diff --git a/chromium/components/autofill_assistant/browser/state.h b/chromium/components/autofill_assistant/browser/state.h
index d7479a72727..3cad37daf2b 100644
--- a/chromium/components/autofill_assistant/browser/state.h
+++ b/chromium/components/autofill_assistant/browser/state.h
@@ -81,14 +81,13 @@ enum class AutofillAssistantState {
// Autofill assistant is stopped, but the controller is still available.
//
// This is a final state for the UI, which, when entering this state, detaches
- // itself from the controller, waits for a few seconds to let the user read
- // the message and then disappears.
+ // itself from the controller and lets the user read the message.
//
// In that scenario, the status message at the time of transition to STOPPED
// is supposed to contain the final message.
//
- // Next states: TRACKING.
- STOPPED
+ // Next states: TRACKING
+ STOPPED,
};
inline std::ostream& operator<<(std::ostream& out,
diff --git a/chromium/components/autofill_assistant/browser/trigger_context.cc b/chromium/components/autofill_assistant/browser/trigger_context.cc
index c872b7e5315..a2e13813571 100644
--- a/chromium/components/autofill_assistant/browser/trigger_context.cc
+++ b/chromium/components/autofill_assistant/browser/trigger_context.cc
@@ -62,6 +62,10 @@ bool TriggerContextImpl::is_cct() const {
return cct_;
}
+bool TriggerContextImpl::is_onboarding_shown() const {
+ return onboarding_shown_;
+}
+
bool TriggerContextImpl::is_direct_action() const {
return direct_action_;
}
@@ -112,6 +116,14 @@ bool MergedTriggerContext::is_cct() const {
return false;
}
+bool MergedTriggerContext::is_onboarding_shown() const {
+ for (const TriggerContext* context : contexts_) {
+ if (context->is_onboarding_shown())
+ return true;
+ }
+ return false;
+}
+
bool MergedTriggerContext::is_direct_action() const {
for (const TriggerContext* context : contexts_) {
if (context->is_direct_action())
diff --git a/chromium/components/autofill_assistant/browser/trigger_context.h b/chromium/components/autofill_assistant/browser/trigger_context.h
index 9d887b7bd03..11e78d111c1 100644
--- a/chromium/components/autofill_assistant/browser/trigger_context.h
+++ b/chromium/components/autofill_assistant/browser/trigger_context.h
@@ -53,6 +53,10 @@ class TriggerContext {
// Java.
virtual bool is_cct() const = 0;
+ // Returns true if the onboarding was shown at the beginning when this
+ // autofill assistant flow got triggered.
+ virtual bool is_onboarding_shown() const = 0;
+
// Returns true if the current action was triggered by a direct action.
virtual bool is_direct_action() const = 0;
};
@@ -76,10 +80,12 @@ class TriggerContextImpl : public TriggerContext {
const std::string& name) const override;
void SetCCT(bool value) { cct_ = value; }
+ void SetOnboardingShown(bool value) { onboarding_shown_ = value; }
void SetDirectAction(bool value) { direct_action_ = value; }
std::string experiment_ids() const override;
bool is_cct() const override;
+ bool is_onboarding_shown() const override;
bool is_direct_action() const override;
private:
@@ -93,6 +99,8 @@ class TriggerContextImpl : public TriggerContext {
bool cct_ = false;
bool direct_action_ = false;
+
+ bool onboarding_shown_ = false;
};
// Merges several TriggerContexts together.
@@ -110,6 +118,7 @@ class MergedTriggerContext : public TriggerContext {
const std::string& name) const override;
std::string experiment_ids() const override;
bool is_cct() const override;
+ bool is_onboarding_shown() const override;
bool is_direct_action() const override;
private:
diff --git a/chromium/components/autofill_assistant/browser/trigger_context_unittest.cc b/chromium/components/autofill_assistant/browser/trigger_context_unittest.cc
index d98b51157db..890841d2d54 100644
--- a/chromium/components/autofill_assistant/browser/trigger_context_unittest.cc
+++ b/chromium/components/autofill_assistant/browser/trigger_context_unittest.cc
@@ -102,6 +102,28 @@ TEST(TriggerContextText, MergeCCT) {
EXPECT_TRUE(one_with_cct->is_cct());
}
+TEST(TriggerContextTest, OnboardingShown) {
+ TriggerContextImpl context;
+
+ EXPECT_FALSE(context.is_onboarding_shown());
+ context.SetOnboardingShown(true);
+ EXPECT_TRUE(context.is_onboarding_shown());
+}
+
+TEST(TriggerContextTest, MergeOnboardingShown) {
+ auto empty = TriggerContext::CreateEmpty();
+
+ auto all_empty = TriggerContext::Merge({empty.get(), empty.get()});
+ EXPECT_FALSE(all_empty->is_onboarding_shown());
+
+ TriggerContextImpl onboarding_context;
+ onboarding_context.SetOnboardingShown(true);
+ auto one_with_onboarding =
+ TriggerContext::Merge({empty.get(), &onboarding_context, empty.get()});
+
+ EXPECT_TRUE(one_with_onboarding->is_onboarding_shown());
+}
+
TEST(TriggerContextText, DirectAction) {
TriggerContextImpl context;
diff --git a/chromium/components/autofill_assistant/browser/ui_delegate.h b/chromium/components/autofill_assistant/browser/ui_delegate.h
index b6d5f7c2c31..63ff2f17465 100644
--- a/chromium/components/autofill_assistant/browser/ui_delegate.h
+++ b/chromium/components/autofill_assistant/browser/ui_delegate.h
@@ -108,8 +108,11 @@ class UiDelegate {
virtual void SetContactInfo(
std::unique_ptr<autofill::AutofillProfile> profile) = 0;
- // Sets credit card, in response to the current collect user data options.
- virtual void SetCreditCard(std::unique_ptr<autofill::CreditCard> card) = 0;
+ // Sets credit card and billing profile, in response to the current collect
+ // user data options.
+ virtual void SetCreditCard(
+ std::unique_ptr<autofill::CreditCard> card,
+ std::unique_ptr<autofill::AutofillProfile> billing_profile) = 0;
// Sets the state of the third party terms & conditions, pertaining to the
// current collect user data options.
@@ -123,6 +126,26 @@ class UiDelegate {
// Called when the user clicks a link on the terms & conditions message.
virtual void OnTermsAndConditionsLinkClicked(int link) = 0;
+ // Sets the start of the date/time range.
+ virtual void SetDateTimeRangeStart(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) = 0;
+
+ // Sets the end of the date/time range.
+ virtual void SetDateTimeRangeEnd(int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second) = 0;
+
+ // Sets an additional value.
+ virtual void SetAdditionalValue(const std::string& client_memory_key,
+ const std::string& value) = 0;
+
// Adds the rectangles that correspond to the current touchable area to
// the given vector.
//
diff --git a/chromium/components/autofill_assistant/browser/user_data.cc b/chromium/components/autofill_assistant/browser/user_data.cc
index c518bdfaf26..2ba170bfa34 100644
--- a/chromium/components/autofill_assistant/browser/user_data.cc
+++ b/chromium/components/autofill_assistant/browser/user_data.cc
@@ -10,10 +10,19 @@
namespace autofill_assistant {
-LoginChoice::LoginChoice(const std::string& id,
- const std::string& text,
- int priority)
- : identifier(id), label(text), preselect_priority(priority) {}
+LoginChoice::LoginChoice(const std::string& _identifier,
+ const std::string& _label,
+ const std::string& _sublabel,
+ const std::string& _sublabel_accessibility_hint,
+ int _preselect_priority,
+ const base::Optional<InfoPopupProto>& _info_popup)
+ : identifier(_identifier),
+ label(_label),
+ sublabel(_sublabel),
+ sublabel_accessibility_hint(_sublabel_accessibility_hint),
+ preselect_priority(_preselect_priority),
+ info_popup(_info_popup) {}
+LoginChoice::LoginChoice(const LoginChoice& another) = default;
LoginChoice::~LoginChoice() = default;
UserData::UserData() = default;
diff --git a/chromium/components/autofill_assistant/browser/user_data.h b/chromium/components/autofill_assistant/browser/user_data.h
index 4e394741e4e..4b1815d0aab 100644
--- a/chromium/components/autofill_assistant/browser/user_data.h
+++ b/chromium/components/autofill_assistant/browser/user_data.h
@@ -5,11 +5,13 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_USER_DATA_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_USER_DATA_H_
+#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
+#include "base/optional.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/user_action.h"
@@ -29,18 +31,35 @@ enum TermsAndConditionsState {
REQUIRES_REVIEW = 2,
};
+// GENERATED_JAVA_ENUM_PACKAGE: (
+// org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections)
+// GENERATED_JAVA_CLASS_NAME_OVERRIDE: AssistantTextInputType
+enum TextInputType { INPUT_TEXT = 0, INPUT_ALPHANUMERIC = 1 };
+
// Represents a concrete login choice in the UI, e.g., 'Guest checkout' or
// a particular Chrome PWM login account.
struct LoginChoice {
- LoginChoice(const std::string& id, const std::string& text, int priority);
+ LoginChoice(const std::string& id,
+ const std::string& label,
+ const std::string& sublabel,
+ const std::string& sublabel_accessibility_hint,
+ int priority,
+ const base::Optional<InfoPopupProto>& info_popup);
+ LoginChoice(const LoginChoice& another);
~LoginChoice();
// Uniquely identifies this login choice.
std::string identifier;
// The label to display to the user.
std::string label;
+ // The sublabel to display to the user.
+ std::string sublabel;
+ // The a11y hint for |sublabel|.
+ std::string sublabel_accessibility_hint;
// The priority to pre-select this choice (-1 == not set/automatic).
int preselect_priority = -1;
+ // The popup to show to provide more information about this login choice.
+ base::Optional<InfoPopupProto> info_popup;
};
// Struct for holding the user data.
@@ -55,6 +74,11 @@ struct UserData {
std::unique_ptr<autofill::AutofillProfile> billing_address;
std::string login_choice_identifier;
TermsAndConditionsState terms_and_conditions = NOT_SELECTED;
+ DateTimeProto date_time_range_start;
+ DateTimeProto date_time_range_end;
+
+ // A set of additional key/value pairs to be stored in client_memory.
+ std::map<std::string, std::string> additional_values_to_store;
};
// Struct for holding the payment request options.
@@ -68,12 +92,15 @@ struct CollectUserDataOptions {
bool request_shipping = false;
bool request_payment_method = false;
bool request_login_choice = false;
+ bool request_date_time_range = false;
bool require_billing_postal_code = false;
std::string billing_postal_code_missing_text;
// If empty, terms and conditions should not be shown.
std::string accept_terms_and_conditions_text;
+ std::string terms_require_review_text;
+ std::string thirdparty_privacy_notice_text;
bool show_terms_as_checkbox = false;
std::vector<std::string> supported_basic_card_networks;
@@ -83,6 +110,9 @@ struct CollectUserDataOptions {
UserActionProto confirm_action;
std::vector<UserActionProto> additional_actions;
TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED;
+ DateTimeRangeProto date_time_range;
+ std::vector<UserFormSectionProto> additional_prepended_sections;
+ std::vector<UserFormSectionProto> additional_appended_sections;
base::OnceCallback<void(std::unique_ptr<UserData>)> confirm_callback;
base::OnceCallback<void(int)> additional_actions_callback;
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder.cc b/chromium/components/autofill_assistant/browser/web/element_finder.cc
index 86fadcce821..158253d4429 100644
--- a/chromium/components/autofill_assistant/browser/web/element_finder.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_finder.cc
@@ -135,10 +135,11 @@ void ElementFinder::Start(Callback callback) {
SendResult(ClientStatus(INVALID_SELECTOR));
return;
}
+
devtools_client_->GetRuntime()->Evaluate(
- std::string(kGetDocumentElement),
+ std::string(kGetDocumentElement), /* node_frame_id= */ std::string(),
base::BindOnce(&ElementFinder::OnGetDocumentElement,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(), 0));
}
void ElementFinder::SendResult(const ClientStatus& status) {
@@ -148,6 +149,7 @@ void ElementFinder::SendResult(const ClientStatus& status) {
}
void ElementFinder::OnGetDocumentElement(
+ size_t index,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result) {
ClientStatus status =
@@ -163,10 +165,14 @@ void ElementFinder::OnGetDocumentElement(
SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
return;
}
- element_result_->container_frame_host = web_contents_->GetMainFrame();
- element_result_->container_frame_selector_index = 0;
- element_result_->object_id = "";
- RecursiveFindElement(object_id, 0);
+
+ element_result_->container_frame_selector_index = index;
+ if (element_result_->container_frame_host == nullptr) {
+ // Don't overwrite results from previous OOPIF passes.
+ element_result_->container_frame_host = web_contents_->GetMainFrame();
+ }
+ element_result_->object_id = std::string();
+ RecursiveFindElement(object_id, index);
}
void ElementFinder::RecursiveFindElement(const std::string& object_id,
@@ -214,6 +220,7 @@ void ElementFinder::RecursiveFindElement(const std::string& object_id,
.SetArguments(std::move(argument))
.SetFunctionDeclaration(function)
.Build(),
+ element_result_->node_frame_id,
base::BindOnce(&ElementFinder::OnQuerySelectorAll,
weak_ptr_factory_.GetWeakPtr(), index));
}
@@ -273,6 +280,7 @@ void ElementFinder::OnQuerySelectorAll(
devtools_client_->GetDOM()->DescribeNode(
dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+ element_result_->node_frame_id,
base::BindOnce(&ElementFinder::OnDescribeNodeForPseudoElement,
weak_ptr_factory_.GetWeakPtr(), pseudo_type));
return;
@@ -280,6 +288,7 @@ void ElementFinder::OnQuerySelectorAll(
devtools_client_->GetDOM()->DescribeNode(
dom::DescribeNodeParams::Builder().SetObjectId(object_id).Build(),
+ element_result_->node_frame_id,
base::BindOnce(&ElementFinder::OnDescribeNode,
weak_ptr_factory_.GetWeakPtr(), object_id, index));
}
@@ -303,6 +312,7 @@ void ElementFinder::OnDescribeNodeForPseudoElement(
dom::ResolveNodeParams::Builder()
.SetBackendNodeId(pseudo_element->GetBackendNodeId())
.Build(),
+ element_result_->node_frame_id,
base::BindOnce(&ElementFinder::OnResolveNodeForPseudoElement,
weak_ptr_factory_.GetWeakPtr()));
return;
@@ -336,39 +346,67 @@ void ElementFinder::OnDescribeNode(
auto* node = result->GetNode();
std::vector<int> backend_ids;
+
if (node->HasContentDocument()) {
+ // If the frame has a ContentDocument, it's considered a local frame.
+ // We need to resolve the RenderFrameHost for autofill.
+
backend_ids.emplace_back(node->GetContentDocument()->GetBackendNodeId());
element_result_->container_frame_selector_index = index;
- // Find out the corresponding render frame host through document url and
- // name.
- // TODO(crbug.com/806868): Use more attributes to find out the render frame
- // host if name and document url are not enough to uniquely identify it.
- std::string frame_name;
- if (node->HasAttributes()) {
- const std::vector<std::string>* attributes = node->GetAttributes();
- for (size_t i = 0; i < attributes->size();) {
- if ((*attributes)[i] == "name") {
- frame_name = (*attributes)[i + 1];
- break;
+ if (node->HasFrameId()) {
+ element_result_->container_frame_host =
+ FindCorrespondingRenderFrameHost(node->GetFrameId());
+ } else {
+ // TODO(b/143318024): Remove the fallback.
+ std::string frame_name;
+ if (node->HasAttributes()) {
+ const std::vector<std::string>* attributes = node->GetAttributes();
+ for (size_t i = 0; i < attributes->size();) {
+ if ((*attributes)[i] == "name") {
+ frame_name = (*attributes)[i + 1];
+ break;
+ }
+ // Jump two positions since attribute name and value are always
+ // paired.
+ i = i + 2;
}
- // Jump two positions since attribute name and value are always paired.
- i = i + 2;
}
+ element_result_->container_frame_host = FindCorrespondingRenderFrameHost(
+ frame_name, node->GetContentDocument()->GetDocumentURL());
}
- element_result_->container_frame_host = FindCorrespondingRenderFrameHost(
- frame_name, node->GetContentDocument()->GetDocumentURL());
+
if (!element_result_->container_frame_host) {
DVLOG(1) << __func__ << " Failed to find corresponding owner frame.";
- SendResult(
- UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
+ SendResult(ClientStatus(FRAME_HOST_NOT_FOUND));
return;
}
} else if (node->HasFrameId()) {
- // TODO(crbug.com/806868): Support out-of-process iframe.
- DVLOG(3) << "Warning (unsupported): the element is inside an OOPIF.";
- SendResult(ClientStatus(UNSUPPORTED));
+ // If the frame has no ContentDocument, it's considered an
+ // OutOfProcessIFrame.
+ // See https://www.chromium.org/developers/design-documents/oop-iframes for
+ // full documentation.
+ // We need to assign the frame id, such that devtools can resolve the
+ // session calls should be executed on. We also need to resolve the
+ // RenderFrameHost for autofill.
+
+ element_result_->node_frame_id = node->GetFrameId();
+ element_result_->container_frame_selector_index = index;
+ element_result_->container_frame_host =
+ FindCorrespondingRenderFrameHost(node->GetFrameId());
+
+ if (!element_result_->container_frame_host) {
+ DVLOG(1) << __func__ << " Failed to find corresponding owner frame.";
+ SendResult(ClientStatus(FRAME_HOST_NOT_FOUND));
+ return;
+ }
+
+ // Kick off another find element chain to walk down the OOP iFrame.
+ devtools_client_->GetRuntime()->Evaluate(
+ std::string(kGetDocumentElement), element_result_->node_frame_id,
+ base::BindOnce(&ElementFinder::OnGetDocumentElement,
+ weak_ptr_factory_.GetWeakPtr(), index + 1));
return;
}
@@ -383,6 +421,7 @@ void ElementFinder::OnDescribeNode(
dom::ResolveNodeParams::Builder()
.SetBackendNodeId(backend_ids[0])
.Build(),
+ element_result_->node_frame_id,
base::BindOnce(&ElementFinder::OnResolveNode,
weak_ptr_factory_.GetWeakPtr(), index));
return;
@@ -405,6 +444,17 @@ void ElementFinder::OnResolveNode(
}
content::RenderFrameHost* ElementFinder::FindCorrespondingRenderFrameHost(
+ std::string frame_id) {
+ for (auto* frame : web_contents_->GetAllFrames()) {
+ if (frame->GetDevToolsFrameToken().ToString() == frame_id) {
+ return frame;
+ }
+ }
+
+ return nullptr;
+}
+
+content::RenderFrameHost* ElementFinder::FindCorrespondingRenderFrameHost(
std::string name,
std::string document_url) {
content::RenderFrameHost* ret_frame = nullptr;
diff --git a/chromium/components/autofill_assistant/browser/web/element_finder.h b/chromium/components/autofill_assistant/browser/web/element_finder.h
index 3acc08bd797..880bc86cf3d 100644
--- a/chromium/components/autofill_assistant/browser/web/element_finder.h
+++ b/chromium/components/autofill_assistant/browser/web/element_finder.h
@@ -44,6 +44,9 @@ class ElementFinder : public WebControllerWorker {
// The object id of the element.
std::string object_id;
+
+ // The id of the frame the element's node is in.
+ std::string node_frame_id;
};
// |web_contents| and |devtools_client| must be valid for the lifetime of the
@@ -62,7 +65,8 @@ class ElementFinder : public WebControllerWorker {
private:
void SendResult(const ClientStatus& status);
- void OnGetDocumentElement(const DevtoolsClient::ReplyStatus& reply_status,
+ void OnGetDocumentElement(size_t index,
+ const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result);
void RecursiveFindElement(const std::string& object_id, size_t index);
void OnQuerySelectorAll(
@@ -83,6 +87,9 @@ class ElementFinder : public WebControllerWorker {
void OnResolveNode(size_t index,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<dom::ResolveNodeResult> result);
+
+ content::RenderFrameHost* FindCorrespondingRenderFrameHost(
+ std::string frame_id);
content::RenderFrameHost* FindCorrespondingRenderFrameHost(
std::string name,
std::string document_url);
@@ -90,6 +97,7 @@ class ElementFinder : public WebControllerWorker {
content::WebContents* const web_contents_;
DevtoolsClient* const devtools_client_;
const Selector selector_;
+
const bool strict_;
Callback callback_;
std::unique_ptr<Result> element_result_;
diff --git a/chromium/components/autofill_assistant/browser/web/element_position_getter.cc b/chromium/components/autofill_assistant/browser/web/element_position_getter.cc
index 09e0aab18e3..6cc0ff3daa4 100644
--- a/chromium/components/autofill_assistant/browser/web/element_position_getter.cc
+++ b/chromium/components/autofill_assistant/browser/web/element_position_getter.cc
@@ -24,11 +24,14 @@ const char* const kScrollIntoViewIfNeededScript =
namespace autofill_assistant {
-ElementPositionGetter::ElementPositionGetter(DevtoolsClient* devtools_client,
- const ClientSettings& settings)
+ElementPositionGetter::ElementPositionGetter(
+ DevtoolsClient* devtools_client,
+ const ClientSettings& settings,
+ const std::string& optional_node_frame_id)
: check_interval_(settings.box_model_check_interval),
max_rounds_(settings.box_model_check_count),
devtools_client_(devtools_client),
+ node_frame_id_(optional_node_frame_id),
weak_ptr_factory_(this) {}
ElementPositionGetter::~ElementPositionGetter() = default;
@@ -64,6 +67,7 @@ void ElementPositionGetter::OnVisualStateUpdatedCallback(bool success) {
void ElementPositionGetter::GetAndWaitBoxModelStable() {
devtools_client_->GetDOM()->GetBoxModel(
dom::GetBoxModelParams::Builder().SetObjectId(object_id_).Build(),
+ node_frame_id_,
base::BindOnce(&ElementPositionGetter::OnGetBoxModelForStableCheck,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -124,13 +128,14 @@ void ElementPositionGetter::OnGetBoxModelForStableCheck(
.SetFunctionDeclaration(std::string(kScrollIntoViewIfNeededScript))
.SetReturnByValue(true)
.Build(),
+ node_frame_id_,
base::BindOnce(&ElementPositionGetter::OnScrollIntoView,
weak_ptr_factory_.GetWeakPtr()));
return;
}
--remaining_rounds_;
- base::PostDelayedTaskWithTraits(
+ base::PostDelayedTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ElementPositionGetter::GetAndWaitBoxModelStable,
weak_ptr_factory_.GetWeakPtr()),
@@ -149,7 +154,7 @@ void ElementPositionGetter::OnScrollIntoView(
}
--remaining_rounds_;
- base::PostDelayedTaskWithTraits(
+ base::PostDelayedTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&ElementPositionGetter::GetAndWaitBoxModelStable,
weak_ptr_factory_.GetWeakPtr()),
diff --git a/chromium/components/autofill_assistant/browser/web/element_position_getter.h b/chromium/components/autofill_assistant/browser/web/element_position_getter.h
index 6639c419ef8..8ed19691250 100644
--- a/chromium/components/autofill_assistant/browser/web/element_position_getter.h
+++ b/chromium/components/autofill_assistant/browser/web/element_position_getter.h
@@ -30,7 +30,8 @@ class ElementPositionGetter : public WebControllerWorker {
public:
// |devtools_client| must be valid for the lifetime of the instance.
ElementPositionGetter(DevtoolsClient* devtools_client,
- const ClientSettings& settings);
+ const ClientSettings& settings,
+ const std::string& optional_node_frame_id);
~ElementPositionGetter() override;
// Callback that receives the position that corresponds to the center
@@ -75,6 +76,8 @@ class ElementPositionGetter : public WebControllerWorker {
int point_x_ = 0;
int point_y_ = 0;
+ std::string node_frame_id_;
+
base::WeakPtrFactory<ElementPositionGetter> weak_ptr_factory_;
};
diff --git a/chromium/components/autofill_assistant/browser/web/mock_web_controller.h b/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
index 03e0fb70e9b..bf89e46bbf6 100644
--- a/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
+++ b/chromium/components/autofill_assistant/browser/web/mock_web_controller.h
@@ -47,24 +47,26 @@ class MockWebController : public WebController {
const TopPadding& top_padding,
base::OnceCallback<void(const ClientStatus&)>& callback));
- void ElementCheck(const Selector& selector,
- bool strict,
- base::OnceCallback<void(bool)> callback) override {
+ void ElementCheck(
+ const Selector& selector,
+ bool strict,
+ base::OnceCallback<void(const ClientStatus&)> callback) override {
OnElementCheck(selector, callback);
}
MOCK_METHOD2(OnElementCheck,
void(const Selector& selector,
- base::OnceCallback<void(bool)>& callback));
+ base::OnceCallback<void(const ClientStatus&)>& callback));
void GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) override {
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback) override {
OnGetFieldValue(selector, callback);
}
- MOCK_METHOD2(
- OnGetFieldValue,
- void(const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)>& callback));
+ MOCK_METHOD2(OnGetFieldValue,
+ void(const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&,
+ const std::string&)>& callback));
void GetVisualViewport(
base::OnceCallback<void(bool, const RectF&)> callback) override {
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller.cc b/chromium/components/autofill_assistant/browser/web/web_controller.cc
index 85c2ae72466..063b22ce2ad 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller.cc
@@ -299,6 +299,7 @@ void WebController::OnFindElementForClickOrTap(
std::string element_object_id = result->object_id;
WaitForDocumentToBecomeInteractive(
settings_->document_ready_check_count, element_object_id,
+ result->node_frame_id,
base::BindOnce(
&WebController::OnWaitDocumentToBecomeInteractiveForClickOrTap,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), click_type,
@@ -333,6 +334,7 @@ void WebController::ClickOrTapElement(
.SetFunctionDeclaration(std::string(kScrollIntoViewCenterScript))
.SetReturnByValue(true)
.Build(),
+ target_element->node_frame_id,
base::BindOnce(&WebController::OnScrollIntoView,
weak_ptr_factory_.GetWeakPtr(), std::move(target_element),
std::move(callback), click_type));
@@ -364,20 +366,22 @@ void WebController::OnScrollIntoView(
.SetArguments(std::move(argument))
.SetFunctionDeclaration(kClickElement)
.Build(),
+ target_element->node_frame_id,
base::BindOnce(&WebController::OnClickJS,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return;
}
std::unique_ptr<ElementPositionGetter> getter =
- std::make_unique<ElementPositionGetter>(devtools_client_.get(),
- *settings_);
+ std::make_unique<ElementPositionGetter>(
+ devtools_client_.get(), *settings_, target_element->node_frame_id);
auto* ptr = getter.get();
pending_workers_.emplace_back(std::move(getter));
- ptr->Start(target_element->container_frame_host, target_element->object_id,
- base::BindOnce(&WebController::TapOrClickOnCoordinates,
- weak_ptr_factory_.GetWeakPtr(), ptr,
- std::move(callback), click_type));
+ ptr->Start(
+ target_element->container_frame_host, target_element->object_id,
+ base::BindOnce(&WebController::TapOrClickOnCoordinates,
+ weak_ptr_factory_.GetWeakPtr(), ptr, std::move(callback),
+ target_element->node_frame_id, click_type));
}
void WebController::OnClickJS(
@@ -395,6 +399,7 @@ void WebController::OnClickJS(
void WebController::TapOrClickOnCoordinates(
ElementPositionGetter* getter_to_release,
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
ClickAction::ClickType click_type,
bool has_coordinates,
int x,
@@ -419,9 +424,10 @@ void WebController::TapOrClickOnCoordinates(
.SetButton(input::DispatchMouseEventButton::LEFT)
.SetType(input::DispatchMouseEventType::MOUSE_PRESSED)
.Build(),
+ node_frame_id,
base::BindOnce(&WebController::OnDispatchPressMouseEvent,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback), x,
- y));
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+ node_frame_id, x, y));
return;
}
@@ -434,12 +440,15 @@ void WebController::TapOrClickOnCoordinates(
.SetType(input::DispatchTouchEventType::TOUCH_START)
.SetTouchPoints(std::move(touch_points))
.Build(),
+ node_frame_id,
base::BindOnce(&WebController::OnDispatchTouchEventStart,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+ node_frame_id));
}
void WebController::OnDispatchPressMouseEvent(
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
int x,
int y,
const DevtoolsClient::ReplyStatus& reply_status,
@@ -460,6 +469,7 @@ void WebController::OnDispatchPressMouseEvent(
.SetButton(input::DispatchMouseEventButton::LEFT)
.SetType(input::DispatchMouseEventType::MOUSE_RELEASED)
.Build(),
+ node_frame_id,
base::BindOnce(&WebController::OnDispatchReleaseMouseEvent,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -479,6 +489,7 @@ void WebController::OnDispatchReleaseMouseEvent(
void WebController::OnDispatchTouchEventStart(
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<input::DispatchTouchEventResult> result) {
if (!result) {
@@ -495,6 +506,7 @@ void WebController::OnDispatchTouchEventStart(
.SetType(input::DispatchTouchEventType::TOUCH_END)
.SetTouchPoints(std::move(touch_points))
.Build(),
+ node_frame_id,
base::BindOnce(&WebController::OnDispatchTouchEventEnd,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -512,9 +524,10 @@ void WebController::OnDispatchTouchEventEnd(
std::move(callback).Run(OkClientStatus());
}
-void WebController::ElementCheck(const Selector& selector,
- bool strict,
- base::OnceCallback<void(bool)> callback) {
+void WebController::ElementCheck(
+ const Selector& selector,
+ bool strict,
+ base::OnceCallback<void(const ClientStatus&)> callback) {
DCHECK(!selector.empty());
FindElement(
selector, strict,
@@ -523,13 +536,13 @@ void WebController::ElementCheck(const Selector& selector,
}
void WebController::OnFindElementForCheck(
- base::OnceCallback<void(bool)> callback,
+ base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status,
std::unique_ptr<ElementFinder::Result> result) {
DVLOG_IF(1,
!status.ok() && status.proto_status() != ELEMENT_RESOLUTION_FAILED)
<< __func__ << ": " << status;
- std::move(callback).Run(status.ok());
+ std::move(callback).Run(status);
}
void WebController::WaitForWindowHeightChange(
@@ -539,6 +552,7 @@ void WebController::WaitForWindowHeightChange(
.SetExpression(kWaitForWindowHeightChange)
.SetAwaitPromise(true)
.Build(),
+ /* node_frame_id= */ std::string(),
base::BindOnce(&WebController::OnWaitForWindowHeightChange,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -576,6 +590,7 @@ void WebController::WaitForDocumentReadyState(
.SetReturnByValue(true)
.SetAwaitPromise(true)
.Build(),
+ /* node_frame_id= */ std::string(),
base::BindOnce(&OnWaitForDocumentReadyState<runtime::EvaluateResult>,
std::move(callback)));
return;
@@ -614,6 +629,7 @@ void WebController::OnFindElementForWaitForDocumentReadyState(
.SetReturnByValue(true)
.SetAwaitPromise(true)
.Build(),
+ element->node_frame_id,
base::BindOnce(
&OnWaitForDocumentReadyState<runtime::CallFunctionOnResult>,
std::move(callback)));
@@ -655,6 +671,7 @@ void WebController::OnFindElementForFocusElement(
std::string element_object_id = element_result->object_id;
WaitForDocumentToBecomeInteractive(
settings_->document_ready_check_count, element_object_id,
+ element_result->node_frame_id,
base::BindOnce(
&WebController::OnWaitDocumentToBecomeInteractiveForFocusElement,
weak_ptr_factory_.GetWeakPtr(), top_padding, std::move(callback),
@@ -690,6 +707,7 @@ void WebController::OnWaitDocumentToBecomeInteractiveForFocusElement(
.SetFunctionDeclaration(std::string(kScrollIntoViewWithPaddingScript))
.SetReturnByValue(true)
.Build(),
+ target_element->node_frame_id,
base::BindOnce(&WebController::OnFocusElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -720,6 +738,23 @@ void WebController::FillAddressForm(
std::move(callback)));
}
+void WebController::FillCardForm(
+ std::unique_ptr<autofill::CreditCard> card,
+ const base::string16& cvc,
+ const Selector& selector,
+ base::OnceCallback<void(const ClientStatus&)> callback) {
+ DVLOG(3) << __func__ << " " << selector;
+ auto data_to_autofill = std::make_unique<FillFormInputData>();
+ data_to_autofill->card = std::move(card);
+ data_to_autofill->cvc = cvc;
+ FindElement(selector,
+ /* strict_mode= */ true,
+ base::BindOnce(&WebController::OnFindElementForFillingForm,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(data_to_autofill), selector,
+ std::move(callback)));
+}
+
void WebController::OnFindElementForFillingForm(
std::unique_ptr<FillFormInputData> data_to_autofill,
const Selector& selector,
@@ -728,12 +763,18 @@ void WebController::OnFindElementForFillingForm(
std::unique_ptr<ElementFinder::Result> element_result) {
if (!status.ok()) {
DVLOG(1) << __func__ << " Failed to find the element for filling the form.";
- std::move(callback).Run(status);
+ std::move(callback).Run(FillAutofillErrorStatus(status));
return;
}
ContentAutofillDriver* driver = ContentAutofillDriver::GetForRenderFrameHost(
element_result->container_frame_host);
+ if (driver == nullptr) {
+ DVLOG(1) << __func__ << " Failed to get the autofill driver.";
+ std::move(callback).Run(
+ FillAutofillErrorStatus(UnexpectedErrorStatus(__FILE__, __LINE__)));
+ return;
+ }
DCHECK(!selector.empty());
// TODO(crbug.com/806868): Figure out whether there are cases where we need
// more than one selector, and come up with a solution that can figure out the
@@ -754,15 +795,17 @@ void WebController::OnGetFormAndFieldDataForFillingForm(
const autofill::FormFieldData& form_field) {
if (form_data.fields.empty()) {
DVLOG(1) << __func__ << " Failed to get form data to fill form.";
- std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+ std::move(callback).Run(
+ FillAutofillErrorStatus(UnexpectedErrorStatus(__FILE__, __LINE__)));
return;
}
ContentAutofillDriver* driver =
ContentAutofillDriver::GetForRenderFrameHost(container_frame_host);
- if (!driver) {
+ if (driver == nullptr) {
DVLOG(1) << __func__ << " Failed to get the autofill driver.";
- std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+ std::move(callback).Run(
+ FillAutofillErrorStatus(UnexpectedErrorStatus(__FILE__, __LINE__)));
return;
}
@@ -778,23 +821,6 @@ void WebController::OnGetFormAndFieldDataForFillingForm(
std::move(callback).Run(OkClientStatus());
}
-void WebController::FillCardForm(
- std::unique_ptr<autofill::CreditCard> card,
- const base::string16& cvc,
- const Selector& selector,
- base::OnceCallback<void(const ClientStatus&)> callback) {
- DVLOG(3) << __func__ << " " << selector;
- auto data_to_autofill = std::make_unique<FillFormInputData>();
- data_to_autofill->card = std::move(card);
- data_to_autofill->cvc = cvc;
- FindElement(selector,
- /* strict_mode= */ true,
- base::BindOnce(&WebController::OnFindElementForFillingForm,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(data_to_autofill), selector,
- std::move(callback)));
-}
-
void WebController::SelectOption(
const Selector& selector,
const std::string& selected_option,
@@ -830,6 +856,7 @@ void WebController::OnFindElementForSelectOption(
.SetFunctionDeclaration(std::string(kSelectOptionScript))
.SetReturnByValue(true)
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnSelectOption,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -891,6 +918,7 @@ void WebController::OnFindElementForHighlightElement(
.SetFunctionDeclaration(std::string(kHighlightElementScript))
.SetReturnByValue(true)
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnHighlightElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -920,7 +948,8 @@ void WebController::FocusElement(
void WebController::GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback) {
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback) {
FindElement(
selector,
/* strict_mode= */ true,
@@ -929,12 +958,12 @@ void WebController::GetFieldValue(
}
void WebController::OnFindElementForGetFieldValue(
- base::OnceCallback<void(bool, const std::string&)> callback,
+ base::OnceCallback<void(const ClientStatus&, const std::string&)> callback,
const ClientStatus& status,
std::unique_ptr<ElementFinder::Result> element_result) {
const std::string object_id = element_result->object_id;
if (!status.ok()) {
- std::move(callback).Run(/* exists= */ false, "");
+ std::move(callback).Run(status, "");
return;
}
@@ -944,12 +973,14 @@ void WebController::OnFindElementForGetFieldValue(
.SetFunctionDeclaration(std::string(kGetValueAttributeScript))
.SetReturnByValue(true)
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnGetValueAttribute,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WebController::OnGetValueAttribute(
- base::OnceCallback<void(bool, const std::string&)> callback,
+ base::OnceCallback<void(const ClientStatus& element_status,
+ const std::string&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result) {
std::string value;
@@ -959,7 +990,7 @@ void WebController::OnGetValueAttribute(
DVLOG_IF(1, !status.ok())
<< __func__ << "Failed to get attribute value: " << status;
SafeGetStringValue(result->GetResult(), &value);
- std::move(callback).Run(/* exists= */ true, value);
+ std::move(callback).Run(status, value);
}
void WebController::SetFieldValue(
@@ -1011,6 +1042,7 @@ void WebController::OnClearFieldForSendKeyboardInput(
}
void WebController::OnClickElementForSendKeyboardInput(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback,
@@ -1019,11 +1051,13 @@ void WebController::OnClickElementForSendKeyboardInput(
std::move(callback).Run(click_status);
return;
}
- DispatchKeyboardTextDownEvent(codepoints, 0, /*delay=*/false,
- delay_in_millisecond, std::move(callback));
+ DispatchKeyboardTextDownEvent(node_frame_id, codepoints, 0,
+ /* delay= */ false, delay_in_millisecond,
+ std::move(callback));
}
void WebController::DispatchKeyboardTextDownEvent(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
size_t index,
bool delay,
@@ -1037,10 +1071,10 @@ void WebController::DispatchKeyboardTextDownEvent(
if (delay && delay_in_millisecond > 0) {
base::PostDelayedTask(
FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
- weak_ptr_factory_.GetWeakPtr(), codepoints, index,
- /*delay=*/false, delay_in_millisecond,
- std::move(callback)),
+ base::BindOnce(
+ &WebController::DispatchKeyboardTextDownEvent,
+ weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints, index,
+ /* delay= */ false, delay_in_millisecond, std::move(callback)),
base::TimeDelta::FromMilliseconds(delay_in_millisecond));
return;
}
@@ -1049,12 +1083,14 @@ void WebController::DispatchKeyboardTextDownEvent(
CreateKeyEventParamsForCharacter(
autofill_assistant::input::DispatchKeyEventType::KEY_DOWN,
codepoints[index]),
+ node_frame_id,
base::BindOnce(&WebController::DispatchKeyboardTextUpEvent,
- weak_ptr_factory_.GetWeakPtr(), codepoints, index,
- delay_in_millisecond, std::move(callback)));
+ weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints,
+ index, delay_in_millisecond, std::move(callback)));
}
void WebController::DispatchKeyboardTextUpEvent(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
size_t index,
int delay_in_millisecond,
@@ -1064,10 +1100,11 @@ void WebController::DispatchKeyboardTextUpEvent(
CreateKeyEventParamsForCharacter(
autofill_assistant::input::DispatchKeyEventType::KEY_UP,
codepoints[index]),
- base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
- weak_ptr_factory_.GetWeakPtr(), codepoints, index + 1,
- /*delay=*/true, delay_in_millisecond,
- std::move(callback)));
+ node_frame_id,
+ base::BindOnce(
+ &WebController::DispatchKeyboardTextDownEvent,
+ weak_ptr_factory_.GetWeakPtr(), node_frame_id, codepoints, index + 1,
+ /* delay= */ true, delay_in_millisecond, std::move(callback)));
}
auto WebController::CreateKeyEventParamsForCharacter(
@@ -1115,6 +1152,7 @@ void WebController::OnFindElementForSetFieldValue(
.SetArguments(std::move(argument))
.SetFunctionDeclaration(std::string(kSetValueAttributeScript))
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnSetValueAttribute,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1175,6 +1213,7 @@ void WebController::OnFindElementForSetAttribute(
.SetArguments(std::move(arguments))
.SetFunctionDeclaration(std::string(kSetAttributeScript))
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnSetAttribute,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1202,8 +1241,7 @@ void WebController::SendKeyboardInput(
DCHECK(!selector.empty());
FindElement(
- selector,
- /* strict_mode= */ true,
+ selector, /* strict_mode= */ true,
base::BindOnce(&WebController::OnFindElementForSendKeyboardInput,
weak_ptr_factory_.GetWeakPtr(), selector, codepoints,
delay_in_millisecond, std::move(callback)));
@@ -1223,7 +1261,8 @@ void WebController::OnFindElementForSendKeyboardInput(
ClickOrTapElement(
selector, ClickAction::CLICK,
base::BindOnce(&WebController::OnClickElementForSendKeyboardInput,
- weak_ptr_factory_.GetWeakPtr(), codepoints,
+ weak_ptr_factory_.GetWeakPtr(),
+ element_result->node_frame_id, codepoints,
delay_in_millisecond, std::move(callback)));
}
@@ -1246,6 +1285,7 @@ void WebController::GetVisualViewport(
.SetExpression(std::string(kGetVisualViewport))
.SetReturnByValue(true)
.Build(),
+ /* node_frame_id= */ std::string(),
base::BindOnce(&WebController::OnGetVisualViewport,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1311,6 +1351,7 @@ void WebController::OnFindElementForPosition(
.SetFunctionDeclaration(std::string(kGetBoundingClientRectAsList))
.SetReturnByValue(true)
.Build(),
+ result->node_frame_id,
base::BindOnce(&WebController::OnGetElementPositionResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1358,6 +1399,7 @@ void WebController::OnFindElementForGetOuterHtml(
.SetFunctionDeclaration(std::string(kGetOuterHtmlScript))
.SetReturnByValue(true)
.Build(),
+ element_result->node_frame_id,
base::BindOnce(&WebController::OnGetOuterHtml,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -1380,7 +1422,8 @@ void WebController::OnGetOuterHtml(
void WebController::WaitForDocumentToBecomeInteractive(
int remaining_rounds,
- std::string object_id,
+ const std::string& object_id,
+ const std::string& node_frame_id,
base::OnceCallback<void(bool)> callback) {
devtools_client_->GetRuntime()->CallFunctionOn(
runtime::CallFunctionOnParams::Builder()
@@ -1388,14 +1431,16 @@ void WebController::WaitForDocumentToBecomeInteractive(
.SetFunctionDeclaration(std::string(kIsDocumentReadyForInteract))
.SetReturnByValue(true)
.Build(),
+ node_frame_id,
base::BindOnce(&WebController::OnWaitForDocumentToBecomeInteractive,
weak_ptr_factory_.GetWeakPtr(), remaining_rounds,
- object_id, std::move(callback)));
+ object_id, node_frame_id, std::move(callback)));
}
void WebController::OnWaitForDocumentToBecomeInteractive(
int remaining_rounds,
- std::string object_id,
+ const std::string& object_id,
+ const std::string& node_frame_id,
base::OnceCallback<void(bool)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result) {
@@ -1420,7 +1465,7 @@ void WebController::OnWaitForDocumentToBecomeInteractive(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&WebController::WaitForDocumentToBecomeInteractive,
weak_ptr_factory_.GetWeakPtr(), --remaining_rounds,
- object_id, std::move(callback)),
+ object_id, node_frame_id, std::move(callback)),
settings_->document_ready_check_interval);
}
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller.h b/chromium/components/autofill_assistant/browser/web/web_controller.h
index 31c32e264ca..713190568c5 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller.h
+++ b/chromium/components/autofill_assistant/browser/web/web_controller.h
@@ -122,7 +122,8 @@ class WebController {
// Normally done through BatchElementChecker.
virtual void GetFieldValue(
const Selector& selector,
- base::OnceCallback<void(bool, const std::string&)> callback);
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback);
// Set the |value| of field |selector| and return the result through
// |callback|. If |simulate_key_presses| is true, the value will be set by
@@ -180,9 +181,10 @@ class WebController {
// pass. Otherwise, there must be at least one.
//
// To check multiple elements, use a BatchElementChecker.
- virtual void ElementCheck(const Selector& selector,
- bool strict,
- base::OnceCallback<void(bool)> callback);
+ virtual void ElementCheck(
+ const Selector& selector,
+ bool strict,
+ base::OnceCallback<void(const ClientStatus&)> callback);
// Calls the callback once the main document window has been resized.
virtual void WaitForWindowHeightChange(
@@ -247,12 +249,14 @@ class WebController {
void TapOrClickOnCoordinates(
ElementPositionGetter* getter_to_release,
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
ClickAction::ClickType click_type,
bool has_coordinates,
int x,
int y);
void OnDispatchPressMouseEvent(
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
int x,
int y,
const DevtoolsClient::ReplyStatus& reply_status,
@@ -263,15 +267,17 @@ class WebController {
std::unique_ptr<input::DispatchMouseEventResult> result);
void OnDispatchTouchEventStart(
base::OnceCallback<void(const ClientStatus&)> callback,
+ const std::string& node_frame_id,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<input::DispatchTouchEventResult> result);
void OnDispatchTouchEventEnd(
base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<input::DispatchTouchEventResult> result);
- void OnFindElementForCheck(base::OnceCallback<void(bool)> callback,
- const ClientStatus& status,
- std::unique_ptr<ElementFinder::Result> result);
+ void OnFindElementForCheck(
+ base::OnceCallback<void(const ClientStatus&)> callback,
+ const ClientStatus& status,
+ std::unique_ptr<ElementFinder::Result> result);
void OnWaitForWindowHeightChange(
base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
@@ -329,11 +335,13 @@ class WebController {
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnFindElementForGetFieldValue(
- base::OnceCallback<void(bool, const std::string&)> callback,
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback,
const ClientStatus& status,
std::unique_ptr<ElementFinder::Result> element_result);
void OnGetValueAttribute(
- base::OnceCallback<void(bool, const std::string&)> callback,
+ base::OnceCallback<void(const ClientStatus&, const std::string&)>
+ callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void InternalSetFieldValue(
@@ -347,17 +355,20 @@ class WebController {
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status);
void OnClickElementForSendKeyboardInput(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& click_status);
void DispatchKeyboardTextDownEvent(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
size_t index,
bool delay,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback);
void DispatchKeyboardTextUpEvent(
+ const std::string& node_frame_id,
const std::vector<UChar32>& codepoints,
size_t index,
int delay_in_milli,
@@ -420,11 +431,13 @@ class WebController {
// Waits for the document.readyState to be 'interactive' or 'complete'.
void WaitForDocumentToBecomeInteractive(
int remaining_rounds,
- std::string object_id,
+ const std::string& object_id,
+ const std::string& node_frame_id,
base::OnceCallback<void(bool)> callback);
void OnWaitForDocumentToBecomeInteractive(
int remaining_rounds,
- std::string object_id,
+ const std::string& object_id,
+ const std::string& node_frame_id,
base::OnceCallback<void(bool)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 9481f480c9c..15bfb057975 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -24,6 +24,8 @@ namespace autofill_assistant {
using ::testing::AnyOf;
using ::testing::IsEmpty;
+// Flag to enable site per process to enforce OOPIFs.
+const char* kSitePerProcess = "site-per-process";
const char* kTargetWebsitePath = "/autofill_assistant_target_website.html";
class WebControllerBrowserTest : public content::ContentBrowserTest,
@@ -32,13 +34,26 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
WebControllerBrowserTest() {}
~WebControllerBrowserTest() override {}
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(kSitePerProcess);
+ }
+
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
+
+ // Start a mock server for hosting an OOPIF.
+ http_server_iframe_ = std::make_unique<net::EmbeddedTestServer>(
+ net::EmbeddedTestServer::TYPE_HTTP);
+ http_server_iframe_->ServeFilesFromSourceDirectory(
+ "components/test/data/autofill_assistant/html_iframe");
+ ASSERT_TRUE(http_server_iframe_->Start(8081));
+
+ // Start the main server hosting the test page.
http_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTP);
http_server_->ServeFilesFromSourceDirectory(
- "components/test/data/autofill_assistant");
- ASSERT_TRUE(http_server_->Start());
+ "components/test/data/autofill_assistant/html");
+ ASSERT_TRUE(http_server_->Start(8080));
ASSERT_TRUE(
NavigateToURL(shell(), http_server_->GetURL(kTargetWebsitePath)));
web_controller_ = WebController::CreateForWebContents(
@@ -46,25 +61,20 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
Observe(shell()->web_contents());
}
- void DidCommitAndDrawCompositorFrame() override {
- paint_occurred_during_last_loop_ = true;
- }
-
- void SetUpCommandLine(base::CommandLine* command_line) override {
- ContentBrowserTest::SetUpCommandLine(command_line);
- command_line->AppendSwitch("allow-pre-commit-input");
- }
-
void WaitTillPageIsIdle(base::TimeDelta continuous_paint_timeout) {
base::TimeTicks finished_load_time = base::TimeTicks::Now();
- bool page_is_loading = false;
- do {
- paint_occurred_during_last_loop_ = false;
- base::RunLoop heart_beat;
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, heart_beat.QuitClosure(), base::TimeDelta::FromSeconds(3));
- heart_beat.Run();
- page_is_loading =
+ while (true) {
+ content::RenderFrameSubmissionObserver frame_submission_observer(
+ web_contents());
+ // Runs a loop for 3 seconds to see if the renderer is idle.
+ {
+ base::RunLoop heart_beat;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, heart_beat.QuitClosure(),
+ base::TimeDelta::FromSeconds(3));
+ heart_beat.Run();
+ }
+ bool page_is_loading =
web_contents()->IsWaitingForResponse() || web_contents()->IsLoading();
if (page_is_loading) {
finished_load_time = base::TimeTicks::Now();
@@ -76,8 +86,12 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
// blinking caret or a persistent animation is making Chrome paint at
// regular intervals. Exit.
break;
+ } else if (frame_submission_observer.render_frame_count() == 0) {
+ // If the renderer has stopped submitting frames for 3 seconds then
+ // we're done.
+ break;
}
- } while (page_is_loading || paint_occurred_during_last_loop_);
+ }
}
void RunStrictElementCheck(const Selector& selector, bool result) {
@@ -114,8 +128,8 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
const Selector& selector,
size_t* pending_number_of_checks_output,
bool expected_result,
- bool result) {
- EXPECT_EQ(expected_result, result) << "selector: " << selector;
+ const ClientStatus& result) {
+ EXPECT_EQ(expected_result, result.ok()) << "selector: " << selector;
*pending_number_of_checks_output -= 1;
if (*pending_number_of_checks_output == 0) {
done_callback.Run();
@@ -150,9 +164,9 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
void OnWaitForElementRemove(const base::Closure& done_callback,
const Selector& selector,
- bool result) {
+ const ClientStatus& result) {
done_callback.Run();
- if (result) {
+ if (result.ok()) {
WaitForElementRemove(selector);
}
}
@@ -315,7 +329,7 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
void OnGetFieldValue(const base::Closure& done_callback,
size_t* pending_number_of_checks_output,
const std::string& expected_value,
- bool exists,
+ const ClientStatus& status,
const std::string& value) {
// Don't use ASSERT_EQ here: if the check fails, this would result in
// an endless loop without meaningful test results.
@@ -461,7 +475,7 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
private:
std::unique_ptr<net::EmbeddedTestServer> http_server_;
- bool paint_occurred_during_last_loop_ = false;
+ std::unique_ptr<net::EmbeddedTestServer> http_server_iframe_;
ClientSettings settings_;
DISALLOW_COPY_AND_ASSIGN(WebControllerBrowserTest);
@@ -487,6 +501,18 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExistenceCheck) {
// A non-existent pseudo-element
RunLaxElementCheck(Selector({"#button"}, AFTER), false);
+
+ // An iFrame.
+ RunLaxElementCheck(Selector({"#iframe"}), true);
+
+ // An element in a same-origin iFrame.
+ RunLaxElementCheck(Selector({"#iframe", "#button"}), true);
+
+ // An OOPIF.
+ RunLaxElementCheck(Selector({"#iframeExternal"}), true);
+
+ // An element in an OOPIF.
+ RunLaxElementCheck(Selector({"#iframeExternal", "#button"}), true);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, VisibilityRequirementCheck) {
@@ -508,6 +534,19 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, VisibilityRequirementCheck) {
// A non-existent pseudo-element
RunLaxElementCheck(Selector({"#button"}, AFTER).MustBeVisible(), false);
+
+ // An iFrame.
+ RunLaxElementCheck(Selector({"#iframe"}).MustBeVisible(), true);
+
+ // An element in a same-origin iFrame.
+ RunLaxElementCheck(Selector({"#iframe", "#button"}).MustBeVisible(), true);
+
+ // An OOPIF.
+ RunLaxElementCheck(Selector({"#iframeExternal"}).MustBeVisible(), true);
+
+ // An element in an OOPIF.
+ RunLaxElementCheck(Selector({"#iframeExternal", "#button"}).MustBeVisible(),
+ true);
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, MultipleVisibleElementCheck) {
@@ -636,6 +675,13 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
selectors.emplace_back(a_selector);
results.emplace_back(true);
+ // OOPIF.
+ a_selector.selectors.clear();
+ a_selector.selectors.emplace_back("#iframeExternal");
+ a_selector.selectors.emplace_back("#button");
+ selectors.emplace_back(a_selector);
+ results.emplace_back(true);
+
// Shadow DOM.
a_selector.selectors.clear();
a_selector.selectors.emplace_back("#iframe");
@@ -690,6 +736,18 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickElementInIFrame) {
WaitForElementRemove(selector);
}
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickElementInOOPIF) {
+ Selector selector;
+ selector.selectors.emplace_back("#iframeExternal");
+ selector.selectors.emplace_back("#button");
+ ClickOrTapElement(selector, ClickAction::CLICK);
+
+ selector.selectors.clear();
+ selector.selectors.emplace_back("#iframeExternal");
+ selector.selectors.emplace_back("#div");
+ WaitForElementRemove(selector);
+}
+
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
ClickElementInScrollContainer) {
// Make sure #scroll_item_3 is not visible, no matter the screen height. It
@@ -1059,6 +1117,28 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetFieldValue) {
.proto_status());
}
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetFieldValueInIFrame) {
+ Selector a_selector;
+
+ // IFrame.
+ a_selector.selectors.clear();
+ a_selector.selectors.emplace_back("#iframe");
+ a_selector.selectors.emplace_back("#input");
+ EXPECT_EQ(ACTION_APPLIED, SetFieldValue(a_selector, "text",
+ /* simulate_key_presses= */ false)
+ .proto_status());
+ GetFieldsValue({a_selector}, {"text"});
+
+ // OOPIF.
+ a_selector.selectors.clear();
+ a_selector.selectors.emplace_back("#iframeExternal");
+ a_selector.selectors.emplace_back("#input");
+ EXPECT_EQ(ACTION_APPLIED, SetFieldValue(a_selector, "text",
+ /* simulate_key_presses= */ false)
+ .proto_status());
+ GetFieldsValue({a_selector}, {"text"});
+}
+
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SendKeyboardInput) {
auto input = UTF8ToUnicode("Zürich");
std::string expected_output = "Zürich";
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_util.cc b/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
index aa0f66d7ab0..f84cbeacf60 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_util.cc
@@ -47,6 +47,13 @@ ClientStatus JavaScriptErrorStatus(
return status;
}
+ClientStatus FillAutofillErrorStatus(ClientStatus status) {
+ status.mutable_details()
+ ->mutable_autofill_error_info()
+ ->set_autofill_error_status(status.proto_status());
+ return status;
+}
+
bool SafeGetObjectId(const runtime::RemoteObject* result, std::string* out) {
if (result && result->HasObjectId()) {
*out = result->GetObjectId();
diff --git a/chromium/components/autofill_assistant/browser/web/web_controller_util.h b/chromium/components/autofill_assistant/browser/web/web_controller_util.h
index b96669513f1..acda6137c8b 100644
--- a/chromium/components/autofill_assistant/browser/web/web_controller_util.h
+++ b/chromium/components/autofill_assistant/browser/web/web_controller_util.h
@@ -53,6 +53,9 @@ ClientStatus CheckJavaScriptResult(
return OkClientStatus();
}
+// Fills a ClientStatus with appropriate details for a Chrome Autofill error.
+ClientStatus FillAutofillErrorStatus(ClientStatus status);
+
// Safely gets an object id from a RemoteObject
bool SafeGetObjectId(const runtime::RemoteObject* result, std::string* out);
diff --git a/chromium/components/autofill_assistant/browser/website_login_fetcher.h b/chromium/components/autofill_assistant/browser/website_login_fetcher.h
index fe8c36df27c..41e11fa8fc8 100644
--- a/chromium/components/autofill_assistant/browser/website_login_fetcher.h
+++ b/chromium/components/autofill_assistant/browser/website_login_fetcher.h
@@ -23,7 +23,7 @@ class WebsiteLoginFetcher {
Login(const Login& other);
~Login();
- // The scheme, host, port and path of the login website.
+ // The origin of the login website.
GURL origin;
std::string username;
};
diff --git a/chromium/components/autofill_assistant/browser/website_login_fetcher_impl.cc b/chromium/components/autofill_assistant/browser/website_login_fetcher_impl.cc
index 8352cedc3fb..4f8d624ad86 100644
--- a/chromium/components/autofill_assistant/browser/website_login_fetcher_impl.cc
+++ b/chromium/components/autofill_assistant/browser/website_login_fetcher_impl.cc
@@ -85,9 +85,9 @@ class WebsiteLoginFetcherImpl::PendingFetchLoginsRequest
// From PendingRequest:
void OnFetchCompleted() override {
std::vector<Login> logins;
- for (const auto& match : form_fetcher_->GetBestMatches()) {
- logins.emplace_back(match.second->origin,
- base::UTF16ToUTF8(match.second->username_value));
+ for (const auto* match : form_fetcher_->GetBestMatches()) {
+ logins.emplace_back(match->origin.GetOrigin(),
+ base::UTF16ToUTF8(match->username_value));
}
std::move(callback_).Run(logins);
PendingRequest::OnFetchCompleted();