Commit bc52bfa0 authored by Stephane Zermatten's avatar Stephane Zermatten Committed by Commit Bot

[Autofill Assistant] Add support for the reset and stop actions.

The reset and stop actions control what happens after the script has
finished executing.

Without any of these, after the script ends, Autofill Assistant checks
for more scripts that can be run, keeping in mind which scripts have
been run and what their status was.

If the script ran a reset action, the memory of which scripts were run
is forgotten. Autofill Assistant restarts on the current URL, as if
nothing had happened.

If the script ran a stop action, Autofill Assistant shuts down after the
end of the script. The UI and everything attached to it goes away.

Bug: 806868
Change-Id: I924aa2e0681c7824fb6fd70e24f1477a997577eb
Reviewed-on: https://chromium-review.googlesource.com/1245796
Commit-Queue: Stephane Zermatten <szermatt@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595032}
parent c4d37b0d
...@@ -53,7 +53,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -53,7 +53,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
parameters.keySet().toArray(new String[parameters.size()]), parameters.keySet().toArray(new String[parameters.size()]),
parameters.values().toArray(new String[parameters.size()])); parameters.values().toArray(new String[parameters.size()]));
// Stop Autofill Assistant when the tab is detached from the activity. // Shut down Autofill Assistant when the tab is detached from the activity.
activityTab.addObserver(new EmptyTabObserver() { activityTab.addObserver(new EmptyTabObserver() {
@Override @Override
public void onActivityAttachmentChanged(Tab tab, boolean isAttached) { public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
...@@ -64,7 +64,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -64,7 +64,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
} }
}); });
// Stop Autofill Assistant when the selected tab (foreground tab) is changed. // Shut down Autofill Assistant when the selected tab (foreground tab) is changed.
TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel(); TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel();
currentTabModel.addObserver(new EmptyTabModelObserver() { currentTabModel.addObserver(new EmptyTabModelObserver() {
@Override @Override
...@@ -121,6 +121,11 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat ...@@ -121,6 +121,11 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
mUiDelegate.hideOverlay(); mUiDelegate.hideOverlay();
} }
@CalledByNative
private void onShutdown() {
mUiDelegate.shutdown();
}
@CalledByNative @CalledByNative
private void onUpdateScripts(String[] scriptNames, String[] scriptPaths) { private void onUpdateScripts(String[] scriptNames, String[] scriptPaths) {
List<AutofillAssistantUiDelegate.ScriptHandle> scriptHandles = new ArrayList<>(); List<AutofillAssistantUiDelegate.ScriptHandle> scriptHandles = new ArrayList<>();
......
...@@ -103,8 +103,7 @@ class AutofillAssistantUiDelegate { ...@@ -103,8 +103,7 @@ class AutofillAssistantUiDelegate {
mBottomBar.findViewById(R.id.close_button).setOnClickListener(new View.OnClickListener() { mBottomBar.findViewById(R.id.close_button).setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
mFullContainer.setVisibility(View.GONE); shutdown();
mClient.onDismiss();
} }
}); });
mBottomBar.findViewById(R.id.feedback_button) mBottomBar.findViewById(R.id.feedback_button)
...@@ -164,4 +163,12 @@ class AutofillAssistantUiDelegate { ...@@ -164,4 +163,12 @@ class AutofillAssistantUiDelegate {
public void hideOverlay() { public void hideOverlay() {
mOverlay.setVisibility(View.GONE); mOverlay.setVisibility(View.GONE);
} }
/**
* Shuts down the Autofill Assistant. The UI disappears and any associated state goes away.
*/
public void shutdown() {
mFullContainer.setVisibility(View.GONE);
mClient.onDismiss();
}
} }
...@@ -87,6 +87,11 @@ void UiControllerAndroid::HideOverlay() { ...@@ -87,6 +87,11 @@ void UiControllerAndroid::HideOverlay() {
AttachCurrentThread(), java_autofill_assistant_ui_controller_); AttachCurrentThread(), java_autofill_assistant_ui_controller_);
} }
void UiControllerAndroid::Shutdown() {
Java_AutofillAssistantUiController_onShutdown(
AttachCurrentThread(), java_autofill_assistant_ui_controller_);
}
void UiControllerAndroid::UpdateScripts( void UiControllerAndroid::UpdateScripts(
const std::vector<ScriptHandle>& scripts) { const std::vector<ScriptHandle>& scripts) {
std::vector<std::string> script_paths; std::vector<std::string> script_paths;
......
...@@ -30,6 +30,7 @@ class UiControllerAndroid : public UiController, public Client { ...@@ -30,6 +30,7 @@ class UiControllerAndroid : public UiController, public Client {
void ShowStatusMessage(const std::string& message) override; void ShowStatusMessage(const std::string& message) override;
void ShowOverlay() override; void ShowOverlay() override;
void HideOverlay() override; void HideOverlay() override;
void Shutdown() override;
void UpdateScripts(const std::vector<ScriptHandle>& scripts) override; void UpdateScripts(const std::vector<ScriptHandle>& scripts) override;
void ChooseAddress( void ChooseAddress(
base::OnceCallback<void(const std::string&)> callback) override; base::OnceCallback<void(const std::string&)> callback) override;
......
...@@ -22,8 +22,12 @@ jumbo_static_library("browser") { ...@@ -22,8 +22,12 @@ jumbo_static_library("browser") {
"actions/click_action.h", "actions/click_action.h",
"actions/focus_element_action.cc", "actions/focus_element_action.cc",
"actions/focus_element_action.h", "actions/focus_element_action.h",
"actions/reset_action.cc",
"actions/reset_action.h",
"actions/select_option_action.cc", "actions/select_option_action.cc",
"actions/select_option_action.h", "actions/select_option_action.h",
"actions/stop_action.cc",
"actions/stop_action.h",
"actions/tell_action.cc", "actions/tell_action.cc",
"actions/tell_action.h", "actions/tell_action.h",
"actions/upload_dom_action.cc", "actions/upload_dom_action.cc",
......
...@@ -90,6 +90,13 @@ class ActionDelegate { ...@@ -90,6 +90,13 @@ class ActionDelegate {
NodeProto* node_tree_out, NodeProto* node_tree_out,
base::OnceCallback<void(bool)> callback) = 0; base::OnceCallback<void(bool)> callback) = 0;
// Shut down Autofill Assistant at the end of the current script.
virtual void Shutdown() = 0;
// Restart Autofill Assistant at the end of the current script with a cleared
// state.
virtual void Restart() = 0;
// Return the current ClientMemory. // Return the current ClientMemory.
virtual ClientMemory* GetClientMemory() = 0; virtual ClientMemory* GetClientMemory() = 0;
......
...@@ -95,6 +95,8 @@ class MockActionDelegate : public ActionDelegate { ...@@ -95,6 +95,8 @@ class MockActionDelegate : public ActionDelegate {
void(const std::vector<std::string>& selectors, void(const std::vector<std::string>& selectors,
NodeProto* node_tree_out, NodeProto* node_tree_out,
base::OnceCallback<void(bool)> callback)); base::OnceCallback<void(bool)> callback));
MOCK_METHOD0(Shutdown, void());
MOCK_METHOD0(Restart, void());
MOCK_METHOD0(GetClientMemory, ClientMemory*()); MOCK_METHOD0(GetClientMemory, ClientMemory*());
}; };
......
// 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/reset_action.h"
#include <memory>
#include <utility>
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
namespace autofill_assistant {
ResetAction::ResetAction(const ActionProto& proto) : Action(proto) {
DCHECK(proto_.has_reset());
}
ResetAction::~ResetAction() {}
void ResetAction::ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) {
delegate->Restart();
processed_action_proto_ = std::make_unique<ProcessedActionProto>();
UpdateProcessedAction(true);
std::move(callback).Run(std::move(processed_action_proto_));
}
} // 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_ACTIONS_RESET_ACTION_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_RESET_ACTION_H_
#include <string>
#include "base/macros.h"
#include "components/autofill_assistant/browser/actions/action.h"
namespace autofill_assistant {
class ResetAction : public Action {
public:
explicit ResetAction(const ActionProto& proto);
~ResetAction() override;
// Overrides Action:
void ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(ResetAction);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_RESET_ACTION_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/actions/stop_action.h"
#include <memory>
#include <utility>
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
namespace autofill_assistant {
StopAction::StopAction(const ActionProto& proto) : Action(proto) {
DCHECK(proto_.has_stop());
}
StopAction::~StopAction() {}
void StopAction::ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) {
delegate->Shutdown();
processed_action_proto_ = std::make_unique<ProcessedActionProto>();
UpdateProcessedAction(true);
std::move(callback).Run(std::move(processed_action_proto_));
}
} // 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_ACTIONS_STOP_ACTION_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_STOP_ACTION_H_
#include <string>
#include "base/macros.h"
#include "components/autofill_assistant/browser/actions/action.h"
namespace autofill_assistant {
class StopAction : public Action {
public:
explicit StopAction(const ActionProto& proto);
~StopAction() override;
// Overrides Action:
void ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(StopAction);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_STOP_ACTION_H_
...@@ -34,9 +34,6 @@ class ClientMemory { ...@@ -34,9 +34,6 @@ class ClientMemory {
virtual void set_selected_address(const std::string& name, virtual void set_selected_address(const std::string& name,
const std::string& guid); const std::string& guid);
// TODO(crbug.com/806868): Add a clear() method that resets the memory and
// call it when necessary (TBD).
private: private:
base::Optional<std::string> selected_card_; base::Optional<std::string> selected_card_;
......
...@@ -56,7 +56,8 @@ Controller::Controller( ...@@ -56,7 +56,8 @@ Controller::Controller(
client_(std::move(client)), client_(std::move(client)),
web_controller_(std::move(web_controller)), web_controller_(std::move(web_controller)),
service_(std::move(service)), service_(std::move(service)),
script_tracker_(std::make_unique<ScriptTracker>(this, this)), script_tracker_(std::make_unique<ScriptTracker>(/* delegate= */ this,
/* listener= */ this)),
parameters_(std::move(parameters)), parameters_(std::move(parameters)),
memory_(std::make_unique<ClientMemory>()), memory_(std::make_unique<ClientMemory>()),
allow_autostart_(true) { allow_autostart_(true) {
...@@ -106,13 +107,32 @@ void Controller::OnGetScripts(const GURL& url, ...@@ -106,13 +107,32 @@ void Controller::OnGetScripts(const GURL& url,
} }
void Controller::OnScriptExecuted(const std::string& script_path, void Controller::OnScriptExecuted(const std::string& script_path,
bool success) { ScriptExecutor::Result result) {
GetUiController()->HideOverlay(); GetUiController()->HideOverlay();
if (!success) { if (!result.success) {
LOG(ERROR) << "Failed to execute script " << script_path; LOG(ERROR) << "Failed to execute script " << script_path;
// TODO(crbug.com/806868): Handle script execution failure. // TODO(crbug.com/806868): Handle script execution failure.
} }
switch (result.at_end) {
case ScriptExecutor::SHUTDOWN:
GetUiController()->Shutdown(); // indirectly deletes this
return;
case ScriptExecutor::RESTART:
script_tracker_ = std::make_unique<ScriptTracker>(/* delegate= */ this,
/* listener= */ this);
memory_ = std::make_unique<ClientMemory>();
script_domain_ = "";
break;
case ScriptExecutor::CONTINUE:
break;
default:
DLOG(ERROR) << "Unexpected value for at_end: " << result.at_end;
break;
}
GetOrCheckScripts(web_contents()->GetLastCommittedURL()); GetOrCheckScripts(web_contents()->GetLastCommittedURL());
} }
......
...@@ -62,7 +62,8 @@ class Controller : public ScriptExecutorDelegate, ...@@ -62,7 +62,8 @@ class Controller : public ScriptExecutorDelegate,
void GetOrCheckScripts(const GURL& url); void GetOrCheckScripts(const GURL& url);
void OnGetScripts(const GURL& url, bool result, const std::string& response); void OnGetScripts(const GURL& url, bool result, const std::string& response);
void OnScriptChosen(const std::string& script_path); void OnScriptChosen(const std::string& script_path);
void OnScriptExecuted(const std::string& script_path, bool success); void OnScriptExecuted(const std::string& script_path,
ScriptExecutor::Result result);
// Overrides content::UiDelegate: // Overrides content::UiDelegate:
void OnClickOverlay() override; void OnClickOverlay() override;
......
...@@ -23,6 +23,7 @@ using ::testing::_; ...@@ -23,6 +23,7 @@ using ::testing::_;
using ::testing::Contains; using ::testing::Contains;
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::Eq; using ::testing::Eq;
using ::testing::InSequence;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::Pair; using ::testing::Pair;
using ::testing::ReturnRef; using ::testing::ReturnRef;
...@@ -101,6 +102,7 @@ class ControllerTest : public content::RenderViewHostTestHarness { ...@@ -101,6 +102,7 @@ class ControllerTest : public content::RenderViewHostTestHarness {
// Updates the current url of the controller and forces a refresh, without // Updates the current url of the controller and forces a refresh, without
// bothering with actually rendering any page content. // bothering with actually rendering any page content.
void SimulateNavigateToUrl(const GURL& url) { void SimulateNavigateToUrl(const GURL& url) {
url_ = url;
tester_->SetLastCommittedURL(url); tester_->SetLastCommittedURL(url);
controller_->DidFinishLoad(nullptr, url); controller_->DidFinishLoad(nullptr, url);
} }
...@@ -163,6 +165,65 @@ TEST_F(ControllerTest, FetchAndRunScripts) { ...@@ -163,6 +165,65 @@ TEST_F(ControllerTest, FetchAndRunScripts) {
SimulateNavigateToUrl(GURL("http://a.example.com/path")); SimulateNavigateToUrl(GURL("http://a.example.com/path"));
} }
TEST_F(ControllerTest, Stop) {
ActionsResponseProto actions_response;
actions_response.add_actions()->mutable_stop();
std::string actions_response_str;
actions_response.SerializeToString(&actions_response_str);
EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _))
.WillOnce(RunOnceCallback<2>(true, actions_response_str));
EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(*mock_ui_controller_, Shutdown());
GetUiDelegate()->OnScriptSelected("stop");
}
TEST_F(ControllerTest, Reset) {
{
InSequence sequence;
// 1. Fetch scripts for URL, which in contains a single "reset" script.
SupportsScriptResponseProto script_response;
AddRunnableScript(&script_response, "reset");
std::string script_response_str;
script_response.SerializeToString(&script_response_str);
EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
.WillOnce(RunOnceCallback<2>(true, script_response_str));
EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
// 2. Execute the "reset" script, which contains a reset action.
ActionsResponseProto actions_response;
actions_response.add_actions()->mutable_reset();
std::string actions_response_str;
actions_response.SerializeToString(&actions_response_str);
EXPECT_CALL(*mock_service_, OnGetActions(StrEq("reset"), _, _))
.WillOnce(RunOnceCallback<2>(true, actions_response_str));
// 3. Report the result of running that action.
EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
// 4. The reset action forces a reload of the scripts, even though the URL
// hasn't changed. The "reset" script is reported again to UpdateScripts.
EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
.WillOnce(RunOnceCallback<2>(true, script_response_str));
// Reset forces the controller to fetch the scripts twice, even though the
// URL doesn't change..
EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
}
// Resetting should clear the client memory
controller_->GetClientMemory()->set_selected_card("set");
SimulateNavigateToUrl(GURL("http://a.example.com/path"));
GetUiDelegate()->OnScriptSelected("reset");
EXPECT_FALSE(controller_->GetClientMemory()->selected_card());
}
TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) { TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) {
EXPECT_CALL(*mock_service_, EXPECT_CALL(*mock_service_,
OnGetScriptsForUrl(Eq(GURL("http://a.example.com/path1")), _, _)) OnGetScriptsForUrl(Eq(GURL("http://a.example.com/path1")), _, _))
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_
#include <string>
#include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "components/autofill_assistant/browser/script.h" #include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/ui_controller.h" #include "components/autofill_assistant/browser/ui_controller.h"
...@@ -21,6 +24,7 @@ class MockUiController : public UiController { ...@@ -21,6 +24,7 @@ class MockUiController : public UiController {
MOCK_METHOD1(ShowStatusMessage, void(const std::string& message)); MOCK_METHOD1(ShowStatusMessage, void(const std::string& message));
MOCK_METHOD0(ShowOverlay, void()); MOCK_METHOD0(ShowOverlay, void());
MOCK_METHOD0(HideOverlay, void()); MOCK_METHOD0(HideOverlay, void());
MOCK_METHOD0(Shutdown, void());
MOCK_METHOD1(UpdateScripts, void(const std::vector<ScriptHandle>& scripts)); MOCK_METHOD1(UpdateScripts, void(const std::vector<ScriptHandle>& scripts));
void ChooseAddress( void ChooseAddress(
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "components/autofill_assistant/browser/actions/autofill_action.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/click_action.h"
#include "components/autofill_assistant/browser/actions/focus_element_action.h" #include "components/autofill_assistant/browser/actions/focus_element_action.h"
#include "components/autofill_assistant/browser/actions/reset_action.h"
#include "components/autofill_assistant/browser/actions/select_option_action.h" #include "components/autofill_assistant/browser/actions/select_option_action.h"
#include "components/autofill_assistant/browser/actions/stop_action.h"
#include "components/autofill_assistant/browser/actions/tell_action.h" #include "components/autofill_assistant/browser/actions/tell_action.h"
#include "components/autofill_assistant/browser/actions/upload_dom_action.h" #include "components/autofill_assistant/browser/actions/upload_dom_action.h"
#include "components/autofill_assistant/browser/actions/wait_for_dom_action.h" #include "components/autofill_assistant/browser/actions/wait_for_dom_action.h"
...@@ -170,16 +172,22 @@ bool ProtocolUtils::ParseActions( ...@@ -170,16 +172,22 @@ bool ProtocolUtils::ParseActions(
actions->emplace_back(std::make_unique<WaitForDomAction>(action)); actions->emplace_back(std::make_unique<WaitForDomAction>(action));
break; break;
} }
case ActionProto::ActionInfoCase::kUploadDom: {
actions->emplace_back(std::make_unique<UploadDomAction>(action));
break;
}
case ActionProto::ActionInfoCase::kSelectOption: { case ActionProto::ActionInfoCase::kSelectOption: {
actions->emplace_back(std::make_unique<SelectOptionAction>(action)); actions->emplace_back(std::make_unique<SelectOptionAction>(action));
break; break;
} }
case ActionProto::ActionInfoCase::kStop: {
actions->emplace_back(std::make_unique<StopAction>(action));
break;
}
case ActionProto::ActionInfoCase::kReset: {
actions->emplace_back(std::make_unique<ResetAction>(action));
break;
}
default:
case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: { case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
LOG(ERROR) << "Unknown or unspported action."; DLOG(ERROR) << "Unknown or unsupported action with action_case="
<< action.action_info_case();
break; break;
} }
} }
......
...@@ -21,7 +21,10 @@ namespace autofill_assistant { ...@@ -21,7 +21,10 @@ namespace autofill_assistant {
ScriptExecutor::ScriptExecutor(const std::string& script_path, ScriptExecutor::ScriptExecutor(const std::string& script_path,
ScriptExecutorDelegate* delegate) ScriptExecutorDelegate* delegate)
: script_path_(script_path), delegate_(delegate), weak_ptr_factory_(this) { : script_path_(script_path),
delegate_(delegate),
at_end_(CONTINUE),
weak_ptr_factory_(this) {
DCHECK(delegate_); DCHECK(delegate_);
} }
ScriptExecutor::~ScriptExecutor() {} ScriptExecutor::~ScriptExecutor() {}
...@@ -112,13 +115,21 @@ void ScriptExecutor::BuildNodeTree(const std::vector<std::string>& selectors, ...@@ -112,13 +115,21 @@ void ScriptExecutor::BuildNodeTree(const std::vector<std::string>& selectors,
std::move(callback)); std::move(callback));
} }
void ScriptExecutor::Shutdown() {
at_end_ = SHUTDOWN;
}
void ScriptExecutor::Restart() {
at_end_ = RESTART;
}
ClientMemory* ScriptExecutor::GetClientMemory() { ClientMemory* ScriptExecutor::GetClientMemory() {
return delegate_->GetClientMemory(); return delegate_->GetClientMemory();
} }
void ScriptExecutor::OnGetActions(bool result, const std::string& response) { void ScriptExecutor::OnGetActions(bool result, const std::string& response) {
if (!result) { if (!result) {
std::move(callback_).Run(false); RunCallback(false);
return; return;
} }
processed_actions_.clear(); processed_actions_.clear();
...@@ -127,19 +138,28 @@ void ScriptExecutor::OnGetActions(bool result, const std::string& response) { ...@@ -127,19 +138,28 @@ void ScriptExecutor::OnGetActions(bool result, const std::string& response) {
bool parse_result = bool parse_result =
ProtocolUtils::ParseActions(response, &last_server_payload_, &actions_); ProtocolUtils::ParseActions(response, &last_server_payload_, &actions_);
if (!parse_result) { if (!parse_result) {
std::move(callback_).Run(false); RunCallback(false);
return; return;
} }
if (actions_.empty()) { if (actions_.empty()) {
// Finished executing the script if there are no more actions. // Finished executing the script if there are no more actions.
std::move(callback_).Run(true); RunCallback(true);
return; return;
} }
ProcessNextAction(); ProcessNextAction();
} }
void ScriptExecutor::RunCallback(bool success) {
DCHECK(callback_);
ScriptExecutor::Result result;
result.success = success;
result.at_end = at_end_;
std::move(callback_).Run(result);
}
void ScriptExecutor::ProcessNextAction() { void ScriptExecutor::ProcessNextAction() {
// We could get into a strange situation if ProcessNextAction is called before // We could get into a strange situation if ProcessNextAction is called before
// the action was reported as processed, which should not happen. In that case // the action was reported as processed, which should not happen. In that case
......
...@@ -27,7 +27,25 @@ class ScriptExecutor : public ActionDelegate { ...@@ -27,7 +27,25 @@ class ScriptExecutor : public ActionDelegate {
ScriptExecutorDelegate* delegate); ScriptExecutorDelegate* delegate);
~ScriptExecutor() override; ~ScriptExecutor() override;
using RunScriptCallback = base::OnceCallback<void(bool)>; // What should happen after the script has run.
enum AtEnd {
// Continue normally.
CONTINUE = 0,
// Shut down Autofill Assistant.
SHUTDOWN,
// Reset all state and restart.
RESTART
};
// Contains the result of the Run operation.
struct Result {
bool success = false;
AtEnd at_end = AtEnd::CONTINUE;
};
using RunScriptCallback = base::OnceCallback<void(Result)>;
void Run(RunScriptCallback callback); void Run(RunScriptCallback callback);
// Override ActionDelegate: // Override ActionDelegate:
...@@ -62,10 +80,13 @@ class ScriptExecutor : public ActionDelegate { ...@@ -62,10 +80,13 @@ class ScriptExecutor : public ActionDelegate {
void BuildNodeTree(const std::vector<std::string>& selectors, void BuildNodeTree(const std::vector<std::string>& selectors,
NodeProto* node_tree_out, NodeProto* node_tree_out,
base::OnceCallback<void(bool)> callback) override; base::OnceCallback<void(bool)> callback) override;
void Shutdown() override;
void Restart() override;
ClientMemory* GetClientMemory() override; ClientMemory* GetClientMemory() override;
private: private:
void OnGetActions(bool result, const std::string& response); void OnGetActions(bool result, const std::string& response);
void RunCallback(bool success);
void ProcessNextAction(); void ProcessNextAction();
void ProcessAction(Action* action); void ProcessAction(Action* action);
void GetNextActions(); void GetNextActions();
...@@ -78,6 +99,7 @@ class ScriptExecutor : public ActionDelegate { ...@@ -78,6 +99,7 @@ class ScriptExecutor : public ActionDelegate {
std::vector<std::unique_ptr<Action>> actions_; std::vector<std::unique_ptr<Action>> actions_;
std::vector<ProcessedActionProto> processed_actions_; std::vector<ProcessedActionProto> processed_actions_;
std::string last_server_payload_; std::string last_server_payload_;
AtEnd at_end_;
base::WeakPtrFactory<ScriptExecutor> weak_ptr_factory_; base::WeakPtrFactory<ScriptExecutor> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ScriptExecutor); DISALLOW_COPY_AND_ASSIGN(ScriptExecutor);
......
...@@ -23,6 +23,7 @@ namespace { ...@@ -23,6 +23,7 @@ namespace {
using ::testing::AllOf; using ::testing::AllOf;
using ::testing::Contains; using ::testing::Contains;
using ::testing::DoAll; using ::testing::DoAll;
using ::testing::Field;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::Pair; using ::testing::Pair;
using ::testing::SaveArg; using ::testing::SaveArg;
...@@ -81,7 +82,10 @@ class ScriptExecutorTest : public testing::Test, public ScriptExecutorDelegate { ...@@ -81,7 +82,10 @@ class ScriptExecutorTest : public testing::Test, public ScriptExecutorDelegate {
TEST_F(ScriptExecutorTest, GetActionsFails) { TEST_F(ScriptExecutorTest, GetActionsFails) {
EXPECT_CALL(mock_service_, OnGetActions(_, _, _)) EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(false, "")); .WillOnce(RunOnceCallback<2>(false, ""));
EXPECT_CALL(executor_callback_, Run(false)); EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, false),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::CONTINUE))));
executor_->Run(executor_callback_.Get()); executor_->Run(executor_callback_.Get());
} }
...@@ -95,11 +99,12 @@ TEST_F(ScriptExecutorTest, ForwardParameters) { ...@@ -95,11 +99,12 @@ TEST_F(ScriptExecutorTest, ForwardParameters) {
_)) _))
.WillOnce(RunOnceCallback<2>(true, "")); .WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_, Run(true)); EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get()); executor_->Run(executor_callback_.Get());
} }
TEST_F(ScriptExecutorTest, RunOneActionReportFailureAndStop) { TEST_F(ScriptExecutorTest, RunOneActionReportAndReturn) {
ActionsResponseProto actions_response; ActionsResponseProto actions_response;
actions_response.set_server_payload("payload"); actions_response.set_server_payload("payload");
actions_response.add_actions() actions_response.add_actions()
...@@ -114,7 +119,10 @@ TEST_F(ScriptExecutorTest, RunOneActionReportFailureAndStop) { ...@@ -114,7 +119,10 @@ TEST_F(ScriptExecutorTest, RunOneActionReportFailureAndStop) {
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _)) EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&processed_actions_capture), .WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
RunOnceCallback<2>(true, ""))); RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true)); EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::CONTINUE))));
executor_->Run(executor_callback_.Get()); executor_->Run(executor_callback_.Get());
ASSERT_EQ(1u, processed_actions_capture.size()); ASSERT_EQ(1u, processed_actions_capture.size());
...@@ -140,13 +148,48 @@ TEST_F(ScriptExecutorTest, RunMultipleActions) { ...@@ -140,13 +148,48 @@ TEST_F(ScriptExecutorTest, RunMultipleActions) {
RunOnceCallback<2>(true, Serialize(next_actions_response)))) RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture), .WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, ""))); RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true)); EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get()); executor_->Run(executor_callback_.Get());
EXPECT_EQ(2u, processed_actions1_capture.size()); EXPECT_EQ(2u, processed_actions1_capture.size());
EXPECT_EQ(1u, processed_actions2_capture.size()); EXPECT_EQ(1u, processed_actions2_capture.size());
} }
TEST_F(ScriptExecutorTest, StopAfterEnd) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()->mutable_stop();
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::SHUTDOWN))));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, ResetAfterEnd) {
ActionsResponseProto actions_response;
actions_response.set_server_payload("payload");
actions_response.add_actions()->mutable_reset();
EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
.WillOnce(RunOnceCallback<2>(true, ""));
EXPECT_CALL(executor_callback_,
Run(AllOf(Field(&ScriptExecutor::Result::success, true),
Field(&ScriptExecutor::Result::at_end,
ScriptExecutor::RESTART))));
executor_->Run(executor_callback_.Get());
}
TEST_F(ScriptExecutorTest, InterruptActionListOnError) { TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
ActionsResponseProto initial_actions_response; ActionsResponseProto initial_actions_response;
initial_actions_response.set_server_payload("payload"); initial_actions_response.set_server_payload("payload");
...@@ -174,7 +217,8 @@ TEST_F(ScriptExecutorTest, InterruptActionListOnError) { ...@@ -174,7 +217,8 @@ TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
RunOnceCallback<2>(true, Serialize(next_actions_response)))) RunOnceCallback<2>(true, Serialize(next_actions_response))))
.WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture), .WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
RunOnceCallback<2>(true, ""))); RunOnceCallback<2>(true, "")));
EXPECT_CALL(executor_callback_, Run(true)); EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
executor_->Run(executor_callback_.Get()); executor_->Run(executor_callback_.Get());
ASSERT_EQ(2u, processed_actions1_capture.size()); ASSERT_EQ(2u, processed_actions1_capture.size());
...@@ -209,7 +253,8 @@ TEST_F(ScriptExecutorTest, RunDelayedAction) { ...@@ -209,7 +253,8 @@ TEST_F(ScriptExecutorTest, RunDelayedAction) {
EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask()); EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
// Moving forward in time triggers action execution. // Moving forward in time triggers action execution.
EXPECT_CALL(executor_callback_, Run(true)); EXPECT_CALL(executor_callback_,
Run(Field(&ScriptExecutor::Result::success, true)));
scoped_task_environment_.FastForwardBy( scoped_task_environment_.FastForwardBy(
base::TimeDelta::FromMilliseconds(1000)); base::TimeDelta::FromMilliseconds(1000));
EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask()); EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
......
...@@ -59,9 +59,11 @@ void ScriptTracker::CheckScripts() { ...@@ -59,9 +59,11 @@ void ScriptTracker::CheckScripts() {
} }
void ScriptTracker::ExecuteScript(const std::string& script_path, void ScriptTracker::ExecuteScript(const std::string& script_path,
base::OnceCallback<void(bool)> callback) { ScriptExecutor::RunScriptCallback callback) {
if (running()) { if (running()) {
std::move(callback).Run(false); ScriptExecutor::Result result;
result.success = false;
std::move(callback).Run(result);
return; return;
} }
...@@ -74,12 +76,10 @@ void ScriptTracker::ExecuteScript(const std::string& script_path, ...@@ -74,12 +76,10 @@ void ScriptTracker::ExecuteScript(const std::string& script_path,
void ScriptTracker::OnScriptRun( void ScriptTracker::OnScriptRun(
const std::string& script_path, const std::string& script_path,
base::OnceCallback<void(bool)> original_callback, ScriptExecutor::RunScriptCallback original_callback,
bool success) { ScriptExecutor::Result result) {
executed_scripts_[script_path] =
success ? SCRIPT_STATUS_SUCCESS : SCRIPT_STATUS_FAILURE;
executor_.reset(); executor_.reset();
std::move(original_callback).Run(success); std::move(original_callback).Run(result);
} }
void ScriptTracker::UpdateRunnableScriptsIfNecessary() { void ScriptTracker::UpdateRunnableScriptsIfNecessary() {
......
...@@ -59,7 +59,7 @@ class ScriptTracker { ...@@ -59,7 +59,7 @@ class ScriptTracker {
// Call CheckScripts to refresh the set of runnable script after script // Call CheckScripts to refresh the set of runnable script after script
// execution. // execution.
void ExecuteScript(const std::string& path, void ExecuteScript(const std::string& path,
base::OnceCallback<void(bool)> callback); ScriptExecutor::RunScriptCallback callback);
// Checks whether a script is currently running. There can be at most one // Checks whether a script is currently running. There can be at most one
// script running at a time. // script running at a time.
...@@ -67,8 +67,8 @@ class ScriptTracker { ...@@ -67,8 +67,8 @@ class ScriptTracker {
private: private:
void OnScriptRun(const std::string& script_path, void OnScriptRun(const std::string& script_path,
base::OnceCallback<void(bool)> original_callback, ScriptExecutor::RunScriptCallback original_callback,
bool success); ScriptExecutor::Result result);
void UpdateRunnableScriptsIfNecessary(); void UpdateRunnableScriptsIfNecessary();
// Returns true if |runnable_| should be updated. // Returns true if |runnable_| should be updated.
......
...@@ -21,11 +21,12 @@ ...@@ -21,11 +21,12 @@
namespace autofill_assistant { namespace autofill_assistant {
using ::testing::_; using ::testing::_;
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre; using ::testing::Field;
using ::testing::IsEmpty; using ::testing::IsEmpty;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::ReturnRef; using ::testing::ReturnRef;
using ::testing::SizeIs; using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
class ScriptTrackerTest : public testing::Test, class ScriptTrackerTest : public testing::Test,
public ScriptTracker::Listener, public ScriptTracker::Listener,
...@@ -227,8 +228,9 @@ TEST_F(ScriptTrackerTest, CheckScriptsAgainAfterScriptEnd) { ...@@ -227,8 +228,9 @@ TEST_F(ScriptTrackerTest, CheckScriptsAgainAfterScriptEnd) {
UnorderedElementsAre("script1", "script2")); UnorderedElementsAre("script1", "script2"));
// run 'script 1' // run 'script 1'
base::MockCallback<base::OnceCallback<void(bool)>> execute_callback; base::MockCallback<ScriptExecutor::RunScriptCallback> execute_callback;
EXPECT_CALL(execute_callback, Run(true)); EXPECT_CALL(execute_callback,
Run(Field(&ScriptExecutor::Result::success, true)));
tracker_.ExecuteScript("script1", execute_callback.Get()); tracker_.ExecuteScript("script1", execute_callback.Get());
tracker_.CheckScripts(); tracker_.CheckScripts();
......
...@@ -193,6 +193,8 @@ message ActionProto { ...@@ -193,6 +193,8 @@ message ActionProto {
UseCreditCardProto use_card = 28; UseCreditCardProto use_card = 28;
UseAddressProto use_address = 29; UseAddressProto use_address = 29;
UploadDomProto upload_dom = 18; UploadDomProto upload_dom = 18;
ResetProto reset = 34;
StopProto stop = 35;
} }
} }
...@@ -354,3 +356,9 @@ message UploadDomProto { ...@@ -354,3 +356,9 @@ message UploadDomProto {
// page is returned. // page is returned.
optional ElementReferenceProto tree_root = 1; optional ElementReferenceProto tree_root = 1;
} }
// Resets Autofill Assistant: clears any state and server payload.
message ResetProto {}
// Stop Autofill Assistant.
message StopProto {}
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_
#include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/ui_delegate.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/ui_delegate.h"
namespace autofill_assistant { namespace autofill_assistant {
struct ScriptHandle; struct ScriptHandle;
...@@ -33,6 +32,11 @@ class UiController { ...@@ -33,6 +32,11 @@ class UiController {
// Hide the overlay. // Hide the overlay.
virtual void HideOverlay() = 0; virtual void HideOverlay() = 0;
// Shuts down Autofill Assistant: hide the UI and frees any associated state.
//
// Warning: this indirectly deletes the caller.
virtual void Shutdown() = 0;
// Update the list of scripts in the UI. // Update the list of scripts in the UI.
virtual void UpdateScripts(const std::vector<ScriptHandle>& scripts) = 0; virtual void UpdateScripts(const std::vector<ScriptHandle>& scripts) = 0;
......
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