Commit 31bc2af9 authored by Stephane Zermatten's avatar Stephane Zermatten Committed by Commit Bot

[Autofill Assistant] Track and report the result of actions.

Extend the AssistantScriptExecutor to store the result of processing
action, successful or not, and report it to the server, which then
decides what new actions to execute.

A notable change is the switch from passing to the actions exactly the
data they need from passing the original action proto, as the
original action proto is necessary anyways to generate a proper result
proto.

This also adds a unit test for the script executor and fix some, but not
all, incompatibilities between the protos defined in assistant.proto and
those on the server.

Change-Id: I190768fb97399daaf347acac762c6ecbb5fe7cad
Bug: 806868
Reviewed-on: https://chromium-review.googlesource.com/1179894
Commit-Queue: Stephane Zermatten <szermatt@google.com>
Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585427}
parent 87182d18
......@@ -272,6 +272,7 @@ test("components_unittests") {
if (is_android) {
deps += [
"//base:base_java_unittest_support",
"//components/autofill_assistant/browser:unit_tests",
"//components/cdm/browser:unit_tests",
"//components/gcm_driver/instance_id:test_support",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
......
......@@ -13,6 +13,7 @@ proto_library("proto") {
jumbo_static_library("browser") {
sources = [
"actions/assistant_action.cc",
"actions/assistant_action.h",
"actions/assistant_action_delegate.h",
"actions/assistant_click_action.cc",
......@@ -53,3 +54,26 @@ jumbo_static_library("browser") {
"//net",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"assistant_script_executor_unittest.cc",
"mock_assistant_service.cc",
"mock_assistant_service.h",
"mock_assistant_ui_controller.cc",
"mock_assistant_ui_controller.h",
"mock_assistant_web_controller.cc",
"mock_assistant_web_controller.h",
"mock_run_once_callback.h",
]
deps = [
":browser",
":proto",
"//base",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
// 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/assistant_action.h"
namespace autofill_assistant {
AssistantAction::AssistantAction(const AssistantActionProto& proto)
: proto_(proto) {}
} // namespace autofill_assistant.
......@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_ACTION_H_
#include "base/callback.h"
#include "components/autofill_assistant/browser/assistant.pb.h"
namespace autofill_assistant {
......@@ -22,8 +23,12 @@ class AssistantAction {
virtual void ProcessAction(AssistantActionDelegate* delegate,
ProcessActionCallback callback) = 0;
const AssistantActionProto& proto() const { return proto_; }
protected:
AssistantAction() = default;
explicit AssistantAction(const AssistantActionProto& proto);
const AssistantActionProto proto_;
};
} // namespace autofill_assistant.
......
......@@ -2,21 +2,28 @@
// 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/assistant_click_action.h"
#include <utility>
#include "components/autofill_assistant/browser/actions/assistant_action_delegate.h"
#include "components/autofill_assistant/browser/actions/assistant_click_action.h"
namespace autofill_assistant {
AssistantClickAction::AssistantClickAction(
const std::vector<std::string>& selectors)
: target_element_selectors_(selectors) {}
AssistantClickAction::AssistantClickAction(const AssistantActionProto& proto)
: AssistantAction(proto) {
DCHECK(proto_.has_click());
}
AssistantClickAction::~AssistantClickAction() {}
void AssistantClickAction::ProcessAction(AssistantActionDelegate* delegate,
ProcessActionCallback callback) {
delegate->ClickElement(target_element_selectors_, std::move(callback));
std::vector<std::string> selectors;
for (const auto& selector : proto_.click().element_to_click().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
delegate->ClickElement(selectors, std::move(callback));
}
} // namespace autofill_assistant.
\ No newline at end of file
} // namespace autofill_assistant.
......@@ -16,9 +16,7 @@ namespace autofill_assistant {
// An action to perform a mouse left button click on a given element on Web.
class AssistantClickAction : public AssistantAction {
public:
// CSS selectors in |selectors| are ordered from top frame to the frame
// contains the element and the element.
explicit AssistantClickAction(const std::vector<std::string>& selectors);
explicit AssistantClickAction(const AssistantActionProto& proto);
~AssistantClickAction() override;
// Overrides AssistantAction:
......@@ -32,4 +30,4 @@ class AssistantClickAction : public AssistantAction {
};
} // namespace autofill_assistant.
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_CLICK_ACTION_H_
\ No newline at end of file
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_CLICK_ACTION_H_
......@@ -3,20 +3,24 @@
// found in the LICENSE file.
#include "components/autofill_assistant/browser/actions/assistant_tell_action.h"
#include "components/autofill_assistant/browser/actions/assistant_action_delegate.h"
#include <utility>
namespace autofill_assistant {
AssistantTellAction::AssistantTellAction(const std::string& message)
: message_(message) {}
AssistantTellAction::AssistantTellAction(const AssistantActionProto& proto)
: AssistantAction(proto) {
DCHECK(proto_.has_tell());
}
AssistantTellAction::~AssistantTellAction() {}
void AssistantTellAction::ProcessAction(AssistantActionDelegate* delegate,
ProcessActionCallback callback) {
delegate->ShowStatusMessage(message_);
// tell.message in the proto is localized.
delegate->ShowStatusMessage(proto_.tell().message());
std::move(callback).Run(true);
}
} // namespace autofill_assistant.
\ No newline at end of file
} // namespace autofill_assistant.
......@@ -15,8 +15,7 @@ namespace autofill_assistant {
// An action to display a message.
class AssistantTellAction : public AssistantAction {
public:
// The |message| is a localized text message from the server to show user.
explicit AssistantTellAction(const std::string& message);
explicit AssistantTellAction(const AssistantActionProto& proto);
~AssistantTellAction() override;
// Overrides AssistantAction:
......@@ -24,10 +23,8 @@ class AssistantTellAction : public AssistantAction {
ProcessActionCallback callback) override;
private:
std::string message_;
DISALLOW_COPY_AND_ASSIGN(AssistantTellAction);
};
} // namespace autofill_assistant.
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_TELL_ACTION_H_
\ No newline at end of file
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_TELL_ACTION_H_
......@@ -9,9 +9,8 @@
namespace autofill_assistant {
AssistantUseAddressAction::AssistantUseAddressAction(
const std::string& usage_message,
const std::vector<std::string>& selectors)
: usage_message_(usage_message), target_element_selectors_(selectors) {}
const AssistantActionProto& proto)
: AssistantAction(proto) {}
AssistantUseAddressAction::~AssistantUseAddressAction() {}
......
......@@ -16,11 +16,8 @@ namespace autofill_assistant {
// An action to ask user to choose a local address to fill the form.
class AssistantUseAddressAction : public AssistantAction {
public:
// The |usage_message| indicates the usage of the address, like billing
// address or shipping address. The |selectors| specifies an element in the
// form to be filled.
AssistantUseAddressAction(const std::string& usage_message,
const std::vector<std::string>& selectors);
explicit AssistantUseAddressAction(const AssistantActionProto& proto);
~AssistantUseAddressAction() override;
// Overrides AssistantAction:
......@@ -28,9 +25,6 @@ class AssistantUseAddressAction : public AssistantAction {
ProcessActionCallback callback) override;
private:
std::string usage_message_;
std::vector<std::string> target_element_selectors_;
DISALLOW_COPY_AND_ASSIGN(AssistantUseAddressAction);
};
......
......@@ -9,8 +9,8 @@
namespace autofill_assistant {
AssistantUseCardAction::AssistantUseCardAction(
const std::vector<std::string>& selectors)
: target_element_selectors_(selectors) {}
const AssistantActionProto& proto)
: AssistantAction(proto) {}
AssistantUseCardAction::~AssistantUseCardAction() {}
......
......@@ -16,8 +16,7 @@ namespace autofill_assistant {
// An action to ask user to choose a local card to fill the form.
class AssistantUseCardAction : public AssistantAction {
public:
// The |selectors| specifies the card number field in the form to be filled.
explicit AssistantUseCardAction(const std::vector<std::string>& selectors);
explicit AssistantUseCardAction(const AssistantActionProto& proto);
~AssistantUseCardAction() override;
// Overrides AssistantAction:
......@@ -25,8 +24,6 @@ class AssistantUseCardAction : public AssistantAction {
ProcessActionCallback callback) override;
private:
std::vector<std::string> target_element_selectors_;
DISALLOW_COPY_AND_ASSIGN(AssistantUseCardAction);
};
......
......@@ -22,21 +22,18 @@ int kDefaultCheckRounds = 150;
namespace autofill_assistant {
AssistantWaitForDomAction::AssistantWaitForDomAction(
int timeout_ms,
const std::vector<std::string>& selectors,
bool for_absence)
: timeout_ms_(timeout_ms),
target_element_selectors_(selectors),
for_absence_(for_absence),
weak_ptr_factory_(this) {}
const AssistantActionProto& proto)
: AssistantAction(proto), weak_ptr_factory_(this) {}
AssistantWaitForDomAction::~AssistantWaitForDomAction() {}
void AssistantWaitForDomAction::ProcessAction(AssistantActionDelegate* delegate,
ProcessActionCallback callback) {
int check_rounds = kDefaultCheckRounds;
if (timeout_ms_ > 0)
check_rounds = std::ceil(timeout_ms_ / kCheckPeriodInMilliseconds);
int timeout_ms = proto_.wait_for_dom().timeout_ms();
if (timeout_ms > 0)
check_rounds = std::ceil(timeout_ms / kCheckPeriodInMilliseconds);
CheckElementExists(delegate, check_rounds, std::move(callback));
}
......@@ -46,9 +43,12 @@ void AssistantWaitForDomAction::CheckElementExists(
int rounds,
ProcessActionCallback callback) {
DCHECK(rounds > 0);
std::vector<std::string> selectors;
for (const auto& selector : proto_.wait_for_dom().element().selectors()) {
selectors.emplace_back(selector);
}
delegate->ElementExists(
target_element_selectors_,
selectors,
base::BindOnce(&AssistantWaitForDomAction::OnCheckElementExists,
weak_ptr_factory_.GetWeakPtr(), delegate, rounds,
std::move(callback)));
......@@ -59,12 +59,13 @@ void AssistantWaitForDomAction::OnCheckElementExists(
int rounds,
ProcessActionCallback callback,
bool result) {
if (for_absence_ && !result) {
bool for_absence = proto_.wait_for_dom().check_for_absence();
if (for_absence && !result) {
std::move(callback).Run(true);
return;
}
if (!for_absence_ && result) {
if (!for_absence && result) {
std::move(callback).Run(true);
return;
}
......
......@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_WAIT_FOR_DOM_ACTION_H_
#include "components/autofill_assistant/browser/actions/assistant_action.h"
#include "components/autofill_assistant/browser/assistant.pb.h"
#include <string>
#include <vector>
......@@ -18,12 +19,7 @@ namespace autofill_assistant {
// An action to ask Chrome to wait for a DOM element to process next action.
class AssistantWaitForDomAction : public AssistantAction {
public:
// |timeout_ms| indicates waiting timeout period. |selectors| specifies the
// DOM element to wait. |for_absence| indicates whether waiting for absence of
// the element.
AssistantWaitForDomAction(int timeout_ms,
const std::vector<std::string>& selectors,
bool for_absence);
explicit AssistantWaitForDomAction(const AssistantActionProto& proto);
~AssistantWaitForDomAction() override;
// Overrides AssistantAction:
......@@ -39,13 +35,9 @@ class AssistantWaitForDomAction : public AssistantAction {
ProcessActionCallback callback,
bool result);
int timeout_ms_;
std::vector<std::string> target_element_selectors_;
bool for_absence_;
base::WeakPtrFactory<AssistantWaitForDomAction> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AssistantWaitForDomAction);
};
} // namespace autofill_assistant.
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_WAIT_FOR_DOM_ACTION_H_
\ No newline at end of file
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ASSISTANT_WAIT_FOR_DOM_ACTION_H_
......@@ -55,6 +55,18 @@ enum PolicyType {
TRACE = 4;
}
message ScriptActionRequestProto {
optional ClientContextProto client_context = 7;
// The server payload received from the previous response.
optional bytes server_payload = 1;
oneof request {
InitialScriptActionsRequestProto initial_request = 4;
NextScriptActionsRequestProto next_request = 5;
}
}
// Initial request to get a script's actions.
message InitialScriptActionsRequestProto {
message QueryProto {
......@@ -66,8 +78,10 @@ message InitialScriptActionsRequestProto {
// Next request to get a script's actions.
message NextScriptActionsRequestProto {
// The server payload received from the previous response.
required bytes server_payload = 1;
// The result of processing each AssistantActionProto from the previous
// response. This field must be in the same order as the actions in the
// original response. It may have less actions in case of failure.
repeated ProcessedAssistantActionProto processed_actions = 1;
}
// Response of a script's actions.
......@@ -83,16 +97,33 @@ message ActionsResponseProto {
// An assistant action could be performed.
message AssistantActionProto {
// Next action id: 3.
// Opaque data that should not be interpreted by the client. The client must
// pass this back unchanged in the next request
optional bytes server_payload = 4;
oneof action_info {
ClickProto click = 1;
TellProto tell = 2;
UseAddressProto use_address = 3;
UseCreditCardProto use_card = 4;
WaitForDomProto wait_for_dom = 5;
ClickProto click = 5;
TellProto tell = 11;
WaitForDomProto wait_for_dom = 19;
UseCreditCardProto use_card = 28;
UseAddressProto use_address = 29;
}
}
message ProcessedAssistantActionProto {
// The action that was processed.
optional AssistantActionProto action = 1;
optional ProcessedAssistantActionStatus status = 2;
}
enum ProcessedAssistantActionStatus {
UNKNOWN_ACTION_STATUS = 0;
ELEMENT_RESOLUTION_FAILED = 1;
ACTION_APPLIED = 2;
OTHER_ACTION_STATUS = 3;
}
// A reference to an unique element on the page, possibly nested in frames.
message ElementReferenceProto {
// A sequence of CSS selectors. Any non-final CSS selector is expected to
......@@ -108,7 +139,7 @@ message ClickProto {
required ElementReferenceProto element_to_click = 1;
}
// Contain a message to tell the user.
// Contain a localized text message from the server to show to the user.
message TellProto {
required string message = 1;
}
......
......@@ -4,6 +4,8 @@
#include "components/autofill_assistant/browser/assistant_protocol_utils.h"
#include <utility>
#include "base/logging.h"
#include "components/autofill_assistant/browser/actions/assistant_click_action.h"
#include "components/autofill_assistant/browser/actions/assistant_tell_action.h"
......@@ -80,26 +82,30 @@ bool AssistantProtocolUtils::ParseAssistantScripts(
// static
std::string AssistantProtocolUtils::CreateInitialScriptActionsRequest(
const std::string& script_path) {
InitialScriptActionsRequestProto::QueryProto query;
query.set_script_path(script_path);
query.set_policy(PolicyType::SCRIPT);
InitialScriptActionsRequestProto initial_request_proto;
initial_request_proto.set_allocated_query(&query);
ScriptActionRequestProto request_proto;
InitialScriptActionsRequestProto::QueryProto* query =
request_proto.mutable_initial_request()->mutable_query();
query->set_script_path(script_path);
query->set_policy(PolicyType::SCRIPT);
std::string serialized_initial_request_proto;
bool success = initial_request_proto.SerializeToString(
&serialized_initial_request_proto);
bool success =
request_proto.SerializeToString(&serialized_initial_request_proto);
DCHECK(success);
return serialized_initial_request_proto;
}
// static
std::string AssistantProtocolUtils::CreateNextScriptActionsRequest(
const std::string& previous_server_payload) {
NextScriptActionsRequestProto request_proto;
const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions) {
ScriptActionRequestProto request_proto;
request_proto.set_server_payload(previous_server_payload);
NextScriptActionsRequestProto* next_request =
request_proto.mutable_next_request();
for (const auto& processed_action : processed_actions) {
next_request->add_processed_actions()->MergeFrom(processed_action);
}
std::string serialized_request_proto;
bool success = request_proto.SerializeToString(&serialized_request_proto);
DCHECK(success);
......@@ -110,8 +116,7 @@ std::string AssistantProtocolUtils::CreateNextScriptActionsRequest(
bool AssistantProtocolUtils::ParseAssistantActions(
const std::string& response,
std::string* return_server_payload,
std::vector<std::unique_ptr<AssistantAction>>* assistant_actions) {
DCHECK(!response.empty());
std::deque<std::unique_ptr<AssistantAction>>* assistant_actions) {
DCHECK(assistant_actions);
ActionsResponseProto response_proto;
......@@ -127,66 +132,28 @@ bool AssistantProtocolUtils::ParseAssistantActions(
for (const auto& action : response_proto.actions()) {
switch (action.action_info_case()) {
case AssistantActionProto::ActionInfoCase::kClick: {
DCHECK(action.has_click());
std::vector<std::string> selectors;
for (const auto& selector :
action.click().element_to_click().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
assistant_actions->emplace_back(
std::make_unique<AssistantClickAction>(selectors));
std::make_unique<AssistantClickAction>(action));
break;
}
case AssistantActionProto::ActionInfoCase::kTell: {
DCHECK(action.has_tell());
assistant_actions->emplace_back(
std::make_unique<AssistantTellAction>(action.tell().message()));
std::make_unique<AssistantTellAction>(action));
break;
}
case AssistantActionProto::ActionInfoCase::kUseAddress: {
DCHECK(action.has_use_address());
std::vector<std::string> selectors;
for (const auto& selector :
action.use_address().form_field_element().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
assistant_actions->emplace_back(
std::make_unique<AssistantUseAddressAction>(
action.use_address().has_usage() ? action.use_address().usage()
: "",
selectors));
std::make_unique<AssistantUseAddressAction>(action));
break;
}
case AssistantActionProto::ActionInfoCase::kUseCard: {
DCHECK(action.has_use_card());
std::vector<std::string> selectors;
for (const auto& selector :
action.use_card().form_field_element().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
assistant_actions->emplace_back(
std::make_unique<AssistantUseCardAction>(selectors));
std::make_unique<AssistantUseCardAction>(action));
break;
}
case AssistantActionProto::ActionInfoCase::kWaitForDom: {
DCHECK(action.has_wait_for_dom());
std::vector<std::string> selectors;
for (const auto& selector :
action.wait_for_dom().element().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
assistant_actions->emplace_back(
std::make_unique<AssistantWaitForDomAction>(
action.wait_for_dom().has_timeout_ms()
? action.wait_for_dom().timeout_ms()
: 0,
selectors,
action.wait_for_dom().has_check_for_absence() &&
action.wait_for_dom().check_for_absence()));
std::make_unique<AssistantUseCardAction>(action));
break;
}
case AssistantActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
......
......@@ -5,14 +5,16 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_PROTOCOL_UTILS_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_PROTOCOL_UTILS_H_
#include "components/autofill_assistant/browser/actions/assistant_action.h"
#include "components/autofill_assistant/browser/assistant_script.h"
#include <deque>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "components/autofill_assistant/browser/actions/assistant_action.h"
#include "components/autofill_assistant/browser/assistant.pb.h"
#include "components/autofill_assistant/browser/assistant_script.h"
class GURL;
namespace autofill_assistant {
......@@ -40,7 +42,8 @@ class AssistantProtocolUtils {
// Create request to get next sequence of actions for a script.
static std::string CreateNextScriptActionsRequest(
const std::string& previous_server_payload);
const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions);
// Parse assistant actions from the given |response|, which should not be an
// empty string.
......@@ -51,7 +54,7 @@ class AssistantProtocolUtils {
static bool ParseAssistantActions(
const std::string& response,
std::string* return_server_payload,
std::vector<std::unique_ptr<AssistantAction>>* assistant_actions);
std::deque<std::unique_ptr<AssistantAction>>* assistant_actions);
private:
// To avoid instantiate this class by accident.
......@@ -60,4 +63,4 @@ class AssistantProtocolUtils {
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_PROTOCOL_UTILS_H_
\ No newline at end of file
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_PROTOCOL_UTILS_H_
......@@ -4,6 +4,9 @@
#include "components/autofill_assistant/browser/assistant_script_executor.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill_assistant/browser/assistant_protocol_utils.h"
......@@ -12,6 +15,7 @@
#include "components/autofill_assistant/browser/assistant_web_controller.h"
namespace autofill_assistant {
AssistantScriptExecutor::AssistantScriptExecutor(
AssistantScript* script,
AssistantScriptExecutorDelegate* delegate)
......@@ -56,8 +60,9 @@ void AssistantScriptExecutor::OnGetAssistantActions(
std::move(callback_).Run(false);
return;
}
processed_actions_.clear();
actions_.clear();
DCHECK(!response.empty());
bool parse_result = AssistantProtocolUtils::ParseAssistantActions(
response, &last_server_payload_, &actions_);
if (!parse_result) {
......@@ -68,30 +73,48 @@ void AssistantScriptExecutor::OnGetAssistantActions(
if (actions_.empty()) {
// Finished executing the script if there are no more actions.
std::move(callback_).Run(true);
return;
}
ProcessNextAction();
}
void AssistantScriptExecutor::ProcessActions(size_t index) {
// Request next sequence of actions after process current sequence of actions.
if (index >= actions_.size()) {
void AssistantScriptExecutor::ProcessNextAction() {
if (actions_.empty()) {
// Request more actions to execute.
GetNextAssistantActions();
return;
}
actions_[index]->ProcessAction(
std::unique_ptr<AssistantAction> action = std::move(actions_.front());
actions_.pop_front();
AssistantAction* action_ptr = action.get();
action_ptr->ProcessAction(
this, base::BindOnce(&AssistantScriptExecutor::OnProcessedAction,
weak_ptr_factory_.GetWeakPtr(), index));
weak_ptr_factory_.GetWeakPtr(), std::move(action)));
}
void AssistantScriptExecutor::GetNextAssistantActions() {}
void AssistantScriptExecutor::GetNextAssistantActions() {
delegate_->GetAssistantService()->GetNextAssistantActions(
last_server_payload_, processed_actions_,
base::BindOnce(&AssistantScriptExecutor::OnGetAssistantActions,
weak_ptr_factory_.GetWeakPtr()));
}
void AssistantScriptExecutor::OnProcessedAction(size_t index, bool status) {
if (!status) {
std::move(callback_).Run(false);
void AssistantScriptExecutor::OnProcessedAction(
std::unique_ptr<AssistantAction> action,
bool success) {
processed_actions_.emplace_back();
ProcessedAssistantActionProto* proto = &processed_actions_.back();
proto->mutable_action()->MergeFrom(action->proto());
proto->set_status(success
? ProcessedAssistantActionStatus::ACTION_APPLIED
: ProcessedAssistantActionStatus::OTHER_ACTION_STATUS);
if (!success) {
// Report error immediately, interrupting action processing.
GetNextAssistantActions();
return;
}
ProcessActions(index++);
ProcessNextAction();
}
} // namespace autofill_assistant
......@@ -5,10 +5,16 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_SCRIPT_EXECUTOR_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ASSISTANT_SCRIPT_EXECUTOR_H_
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/assistant_action.h"
#include "components/autofill_assistant/browser/actions/assistant_action_delegate.h"
#include "components/autofill_assistant/browser/assistant.pb.h"
#include "components/autofill_assistant/browser/assistant_script.h"
#include "components/autofill_assistant/browser/assistant_script_executor_delegate.h"
......@@ -34,15 +40,16 @@ class AssistantScriptExecutor : public AssistantActionDelegate {
private:
void OnGetAssistantActions(bool result, const std::string& response);
void ProcessActions(size_t index);
void ProcessNextAction();
void GetNextAssistantActions();
void OnProcessedAction(size_t index, bool status);
void OnProcessedAction(std::unique_ptr<AssistantAction> action, bool status);
AssistantScript* script_;
AssistantScriptExecutorDelegate* delegate_;
RunScriptCallback callback_;
std::vector<std::unique_ptr<AssistantAction>> actions_;
std::deque<std::unique_ptr<AssistantAction>> actions_;
std::vector<ProcessedAssistantActionProto> processed_actions_;
std::string last_server_payload_;
base::WeakPtrFactory<AssistantScriptExecutor> weak_ptr_factory_;
......
// 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/assistant_script_executor.h"
#include "base/test/mock_callback.h"
#include "components/autofill_assistant/browser/assistant_service.h"
#include "components/autofill_assistant/browser/mock_assistant_service.h"
#include "components/autofill_assistant/browser/mock_assistant_ui_controller.h"
#include "components/autofill_assistant/browser/mock_assistant_web_controller.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
namespace {
using ::testing::DoAll;
using ::testing::SaveArg;
using ::testing::StrEq;
using ::testing::StrictMock;
using ::testing::NiceMock;
using ::testing::_;
class AssistantScriptExecutorTest : public testing::Test,
public AssistantScriptExecutorDelegate {
public:
void SetUp() override {
script_.name = "script name";
script_.path = "script path";
executor_ = std::make_unique<AssistantScriptExecutor>(&script_, this);
// In this test, "tell" actions always succeed and "click" actions always
// fail.
ON_CALL(mock_assistant_web_controller_, OnClickElement(_, _))
.WillByDefault(RunOnceCallback<1>(false));
}
protected:
AssistantScriptExecutorTest() {}
AssistantService* GetAssistantService() override {
return &mock_assistant_service_;
}
AssistantUiController* GetAssistantUiController() override {
return &mock_assistant_ui_controller_;
}
AssistantWebController* GetAssistantWebController() override {
return &mock_assistant_web_controller_;
}
std::string Serialize(const google::protobuf::MessageLite& message) {
std::string output;
message.SerializeToString(&output);
return output;
}
AssistantScript script_;
StrictMock<MockAssistantService> mock_assistant_service_;
NiceMock<MockAssistantWebController> mock_assistant_web_controller_;
NiceMock<MockAssistantUiController> mock_assistant_ui_controller_;
std::unique_ptr<AssistantScriptExecutor> executor_;
StrictMock<base::MockCallback<AssistantScriptExecutor::RunScriptCallback>>
executor_callback_;
};
TEST_F(AssistantScriptExecutorTest, GetAssistantActionsFails) {
EXPECT_CALL(mock_assistant_service_, OnGetAssistantActions(_, _))
.WillOnce(RunOnceCallback<1>(false, ""));
EXPECT_CALL(executor_callback_, Run(false));
executor_->Run(executor_callback_.Get());
}
TEST_F(AssistantScriptExecutorTest, RunOneActionReportFailureAndStop) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()
->mutable_click()
->mutable_element_to_click()
->add_selectors("will fail");
EXPECT_CALL(mock_assistant_service_, OnGetAssistantActions(_, _))
.WillOnce(RunOnceCallback<1>(true, Serialize(actions_response)));
std::vector<ProcessedAssistantActionProto> processed_actions_capture;
EXPECT_CALL(mock_assistant_service_, OnGetNextAssistantActions(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true));
executor_->Run(executor_callback_.Get());
ASSERT_EQ(1u, processed_actions_capture.size());
EXPECT_EQ(OTHER_ACTION_STATUS, processed_actions_capture[0].status());
}
TEST_F(AssistantScriptExecutorTest, RunMultipleActions) {
ActionsResponseProto initial_actions_response;
initial_actions_response.set_server_payload("payload1");
initial_actions_response.add_actions()->mutable_tell()->set_message("1");
initial_actions_response.add_actions()->mutable_tell()->set_message("2");
EXPECT_CALL(mock_assistant_service_,
OnGetAssistantActions(StrEq("script path"), _))
.WillOnce(RunOnceCallback<1>(true, Serialize(initial_actions_response)));
ActionsResponseProto next_actions_response;
next_actions_response.set_server_payload("payload2");
next_actions_response.add_actions()->mutable_tell()->set_message("3");
std::vector<ProcessedAssistantActionProto> processed_actions1_capture;
std::vector<ProcessedAssistantActionProto> processed_actions2_capture;
EXPECT_CALL(mock_assistant_service_, OnGetNextAssistantActions(_, _, _))
.WillOnce(
DoAll(SaveArg<1>(&processed_actions1_capture),
RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true));
executor_->Run(executor_callback_.Get());
EXPECT_EQ(2u, processed_actions1_capture.size());
EXPECT_EQ(1u, processed_actions2_capture.size());
}
TEST_F(AssistantScriptExecutorTest, InterruptActionListOnError) {
ActionsResponseProto initial_actions_response;
initial_actions_response.set_server_payload("payload");
initial_actions_response.add_actions()->mutable_tell()->set_message(
"will pass");
initial_actions_response.add_actions()
->mutable_click()
->mutable_element_to_click()
->add_selectors("will fail");
initial_actions_response.add_actions()->mutable_tell()->set_message(
"never run");
EXPECT_CALL(mock_assistant_service_, OnGetAssistantActions(_, _))
.WillOnce(RunOnceCallback<1>(true, Serialize(initial_actions_response)));
ActionsResponseProto next_actions_response;
next_actions_response.set_server_payload("payload2");
next_actions_response.add_actions()->mutable_tell()->set_message(
"will run after error");
std::vector<ProcessedAssistantActionProto> processed_actions1_capture;
std::vector<ProcessedAssistantActionProto> processed_actions2_capture;
EXPECT_CALL(mock_assistant_service_, OnGetNextAssistantActions(_, _, _))
.WillOnce(
DoAll(SaveArg<1>(&processed_actions1_capture),
RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true));
executor_->Run(executor_callback_.Get());
ASSERT_EQ(2u, processed_actions1_capture.size());
EXPECT_EQ(ACTION_APPLIED, processed_actions1_capture[0].status());
EXPECT_EQ(OTHER_ACTION_STATUS, processed_actions1_capture[1].status());
ASSERT_EQ(1u, processed_actions2_capture.size());
EXPECT_EQ(ACTION_APPLIED, processed_actions2_capture[0].status());
// make sure "never run" wasn't the one that was run.
EXPECT_EQ("will run after error",
processed_actions2_capture[0].action().tell().message());
}
} // namespace
} // namespace autofill_assistant
......@@ -4,6 +4,10 @@
#include "components/autofill_assistant/browser/assistant_service.h"
#include <string>
#include <utility>
#include <vector>
#include "base/strings/strcat.h"
#include "components/autofill_assistant/browser/assistant_protocol_utils.h"
#include "content/public/browser/browser_context.h"
......@@ -92,6 +96,7 @@ void AssistantService::GetAssistantActions(const std::string& script_path,
void AssistantService::GetNextAssistantActions(
const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions,
ResponseCallback callback) {
DCHECK(!previous_server_payload.empty());
......@@ -101,7 +106,7 @@ void AssistantService::GetNextAssistantActions(
assistant_loader->loader = CreateAndStartLoader(
assistant_script_action_server_url_,
AssistantProtocolUtils::CreateNextScriptActionsRequest(
previous_server_payload),
previous_server_payload, processed_actions),
assistant_loader.get());
assistant_loaders_[assistant_loader.get()] = std::move(assistant_loader);
}
......
......@@ -7,8 +7,11 @@
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "components/autofill_assistant/browser/assistant.pb.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
......@@ -21,22 +24,25 @@ namespace autofill_assistant {
// client actions.
class AssistantService {
public:
AssistantService(content::BrowserContext* context);
~AssistantService();
explicit AssistantService(content::BrowserContext* context);
virtual ~AssistantService();
using ResponseCallback =
base::OnceCallback<void(bool result, const std::string&)>;
// Get assistant scripts for a given |url|, which should be a valid URL.
void GetAssistantScriptsForUrl(const GURL& url, ResponseCallback callback);
virtual void GetAssistantScriptsForUrl(const GURL& url,
ResponseCallback callback);
// Get assistant actions.
void GetAssistantActions(const std::string& script_path,
ResponseCallback callback);
virtual void GetAssistantActions(const std::string& script_path,
ResponseCallback callback);
// Get next sequence of assistant actions according to server payload in
// previous reponse.
void GetNextAssistantActions(const std::string& previous_server_payload,
ResponseCallback callback);
virtual void GetNextAssistantActions(
const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions,
ResponseCallback callback);
private:
// Struct to store assistant scripts and actions request.
......
......@@ -16,18 +16,18 @@ namespace autofill_assistant {
class AssistantWebController {
public:
AssistantWebController();
~AssistantWebController();
virtual ~AssistantWebController();
// Perform a moust left button click on the element given by |selectors| and
// return the result through callback.
// CSS selectors in |selectors| are ordered from top frame to the frame
// contains the element and the element.
void ClickElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
virtual void ClickElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
// Check whether the element given by |selectors| exists on the web page.
void ElementExists(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
virtual void ElementExists(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
private:
DISALLOW_COPY_AND_ASSIGN(AssistantWebController);
......
// 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/mock_assistant_service.h"
namespace autofill_assistant {
MockAssistantService::MockAssistantService() : AssistantService(nullptr) {}
MockAssistantService::~MockAssistantService() {}
} // namespace autofill_assistant
// 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_MOCK_ASSISTANT_SERVICE_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_SERVICE_H_
#include <string>
#include "components/autofill_assistant/browser/assistant_service.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
class MockAssistantService : public AssistantService {
public:
MockAssistantService();
~MockAssistantService() override;
void GetAssistantScriptsForUrl(const GURL& url,
ResponseCallback callback) override {
// Transforming callback into a references allows using RunOnceCallback on
// the argument.
OnGetAssistantScriptsForUrl(url, callback);
}
MOCK_METHOD2(OnGetAssistantScriptsForUrl,
void(const GURL& url, ResponseCallback& callback));
void GetAssistantActions(const std::string& script_path,
ResponseCallback callback) override {
OnGetAssistantActions(script_path, callback);
}
MOCK_METHOD2(OnGetAssistantActions,
void(const std::string& script_path,
ResponseCallback& callback));
void GetNextAssistantActions(
const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions,
ResponseCallback callback) override {
OnGetNextAssistantActions(previous_server_payload, processed_actions,
callback);
}
MOCK_METHOD3(
OnGetNextAssistantActions,
void(const std::string& previous_server_payload,
const std::vector<ProcessedAssistantActionProto>& processed_actions,
ResponseCallback& callback));
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_SERVICE_H_
// 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/mock_assistant_ui_controller.h"
namespace autofill_assistant {
MockAssistantUiController::MockAssistantUiController() {}
MockAssistantUiController::~MockAssistantUiController() {}
} // namespace autofill_assistant
// 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_MOCK_ASSISTANT_UI_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_UI_CONTROLLER_H_
#include "components/autofill_assistant/browser/assistant_ui_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
class MockAssistantUiController : public AssistantUiController {
public:
MockAssistantUiController();
~MockAssistantUiController() override;
MOCK_METHOD1(SetUiDelegate, void(AssistantUiDelegate* ui_delegate));
MOCK_METHOD1(ShowStatusMessage, void(const std::string& message));
MOCK_METHOD0(ShowOverlay, void());
MOCK_METHOD0(HideOverlay, void());
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_UI_CONTROLLER_H_
// 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/mock_assistant_web_controller.h"
namespace autofill_assistant {
MockAssistantWebController::MockAssistantWebController() {}
MockAssistantWebController::~MockAssistantWebController() {}
} // namespace autofill_assistant
// 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_MOCK_ASSISTANT_WEB_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_WEB_CONTROLLER_H_
#include <string>
#include <vector>
#include "base/callback.h"
#include "components/autofill_assistant/browser/assistant_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
class MockAssistantWebController : public AssistantWebController {
public:
MockAssistantWebController();
~MockAssistantWebController() override;
void ClickElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override {
// Transforming callback into a references allows using RunOnceCallback on
// the argument.
OnClickElement(selectors, callback);
}
MOCK_METHOD2(OnClickElement,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)>& callback));
void ElementExists(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override {
OnElementExists(selectors, callback);
}
MOCK_METHOD2(OnElementExists,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)>& callback));
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_ASSISTANT_WEB_CONTROLLER_H_
// 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_MOCK_RUN_ONCE_CALLBACK_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_RUN_ONCE_CALLBACK_H_
#include "testing/gmock/include/gmock/gmock.h"
namespace autofill_assistant {
// Templates for calling base::OnceCallback from gmock actions.
//
// To work around the fact that OnceCallback can't be copied, the method
// to be mocked needs to take the callback as a reference. To do it without
// changing the original interface, follow this pattern:
//
// void DoSomething(..., base::OnceCallback<void(bool)> callback) override {
// OnDoSomething(..., callback);
// }
// MOCK_METHOD2(OnDoSomething,
// void(..., base::OnceCallback<void(bool)>& callback));
//
//
ACTION_TEMPLATE(RunOnceCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p0)) {
return std::move(std::get<k>(args)).Run(p0);
}
ACTION_TEMPLATE(RunOnceCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_2_VALUE_PARAMS(p0, p1)) {
return std::move(std::get<k>(args)).Run(p0, p1);
}
ACTION_TEMPLATE(RunOnceCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_3_VALUE_PARAMS(p0, p1, p2)) {
return std::move(std::get<k>(args)).Run(p0, p1, p2);
}
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_RUN_ONCE_CALLBACK_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment