blob: ddf1045a4db6061bb0fc749d198f3fe6550d78f0 [file] [log] [blame]
// 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_SCRIPT_EXECUTOR_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_EXECUTOR_H_
#include <deque>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/details.h"
#include "components/autofill_assistant/browser/info_box.h"
#include "components/autofill_assistant/browser/retry_timer.h"
#include "components/autofill_assistant/browser/script.h"
#include "components/autofill_assistant/browser/script_executor_delegate.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant {
// Class to execute an assistant script.
class ScriptExecutor : public ActionDelegate {
public:
// Listens to events on ScriptExecutor.
// TODO(b/806868): Make global_payload a part of callback instead of the
// listener.
class Listener {
public:
virtual ~Listener() = default;
// Called when new server payloads are available.
//
// TODO(b/806868): Stop reporting the script payload once the server has
// transitioned to global payloads.
virtual void OnServerPayloadChanged(const std::string& global_payload,
const std::string& script_payload) = 0;
// Called when an update list of scripts is available.
virtual void OnScriptListChanged(
std::vector<std::unique_ptr<Script>> scripts) = 0;
};
// |delegate|, |listener|, |script_state| and |ordered_interrupts| should
// outlive this object and should not be nullptr.
ScriptExecutor(const std::string& script_path,
const std::string& global_payload,
const std::string& script_payload,
ScriptExecutor::Listener* listener,
std::map<std::string, ScriptStatusProto>* scripts_state,
const std::vector<Script*>* ordered_interrupts,
ScriptExecutorDelegate* delegate);
~ScriptExecutor() override;
// What should happen after the script has run.
enum AtEnd {
// Continue normally.
CONTINUE = 0,
// Shut down Autofill Assistant.
SHUTDOWN,
// Shut down Autofill Assistant after a delay.
SHUTDOWN_GRACEFULLY,
// Shut down Autofill Assistant and CCT.
CLOSE_CUSTOM_TAB,
// Reset all state and restart.
RESTART,
// Autofill Assistant is shutting down.
//
// Returned after ScriptExecutor::Terminate has been called while running a
// script.
TERMINATE,
};
// Contains the result of the Run operation.
struct Result {
bool success = false;
AtEnd at_end = AtEnd::CONTINUE;
std::unique_ptr<ElementAreaProto> touchable_element_area;
Result();
~Result();
friend std::ostream& operator<<(std::ostream& out, const Result& result);
};
using RunScriptCallback = base::OnceCallback<void(const Result&)>;
void Run(RunScriptCallback callback);
// Terminates the running scripts. The script finishes running the current
// action, then returns a result with at_end set to TERMINATE.
void Terminate();
// Override ActionDelegate:
void RunElementChecks(BatchElementChecker* checker,
base::OnceCallback<void()> all_done) override;
void ShortWaitForElement(const Selector& selector,
base::OnceCallback<void(bool)> callback) override;
void WaitForDom(
base::TimeDelta max_wait_time,
bool allow_interrupt,
ActionDelegate::SelectorPredicate selector_predicate,
const Selector& selector,
base::OnceCallback<void(ProcessedActionStatusProto)> callback) override;
void SetStatusMessage(const std::string& message) override;
std::string GetStatusMessage() override;
void ClickOrTapElement(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void GetPaymentInformation(
std::unique_ptr<PaymentRequestOptions> options) override;
void GetFullCard(GetFullCardCallback callback) override;
void Prompt(std::unique_ptr<std::vector<Chip>> chips,
base::OnceCallback<void()> on_terminate) override;
void CancelPrompt() override;
void FillAddressForm(
const autofill::AutofillProfile* profile,
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void FillCardForm(
std::unique_ptr<autofill::CreditCard> card,
const base::string16& cvc,
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void SelectOption(
const Selector& selector,
const std::string& selected_option,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void HighlightElement(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void FocusElement(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void SetTouchableElementArea(
const ElementAreaProto& touchable_element_area) override;
void GetFieldValue(
const Selector& selector,
base::OnceCallback<void(bool, const std::string&)> callback) override;
void SetFieldValue(
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void SetAttribute(
const Selector& selector,
const std::vector<std::string>& attribute,
const std::string& value,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void GetOuterHtml(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback) override;
void LoadURL(const GURL& url) override;
void Shutdown() override;
void Close() override;
void Restart() override;
ClientMemory* GetClientMemory() override;
autofill::PersonalDataManager* GetPersonalDataManager() override;
content::WebContents* GetWebContents() override;
void SetDetails(std::unique_ptr<Details> details) override;
void ClearInfoBox() override;
void SetInfoBox(const InfoBox& info_box) override;
void SetProgress(int progress) override;
void SetProgressVisible(bool visible) override;
private:
// Helper for WaitForElementVisible that keeps track of the state required to
// run interrupts while waiting for a specific element.
class WaitForDomOperation : public ScriptExecutor::Listener {
public:
// Let the caller know about either the result of looking for the element or
// of an abnormal result from an interrupt.
//
// If the given result is non-null, it should be forwarded as the result of
// the main script.
//
// The third argument contains the set of interrupts that were run while
// waiting.
using Callback = base::OnceCallback<void(bool,
const ScriptExecutor::Result*,
const std::set<std::string>&)>;
// |main_script_| must not be null and outlive this instance.
WaitForDomOperation(ScriptExecutor* main_script,
base::TimeDelta max_wait_time,
bool allow_interrupt,
ActionDelegate::SelectorPredicate selector_predicate,
const Selector& selectors,
WaitForDomOperation::Callback callback);
~WaitForDomOperation() override;
void Run();
void Terminate();
private:
// Implements ScriptExecutor::Listener
void OnServerPayloadChanged(const std::string& global_payload,
const std::string& script_payload) override;
void OnScriptListChanged(
std::vector<std::unique_ptr<Script>> scripts) override;
void RunChecks(base::OnceCallback<void(bool)> 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 RunInterrupt(const Script* interrupt);
void OnInterruptDone(const ScriptExecutor::Result& result);
void RunCallback(bool found);
void RunCallbackWithResult(bool found,
const ScriptExecutor::Result* result);
// Saves the current state and sets save_pre_interrupt_state_.
void SavePreInterruptState();
// Restores the UI states as found by SavePreInterruptState.
void RestoreStatusMessage();
// if save_pre_interrupt_state_ is set, attempt to scroll the page back to
// the original area.
void RestorePreInterruptScroll(bool element_found);
ScriptExecutor* main_script_;
const base::TimeDelta max_wait_time_;
const bool allow_interrupt_;
const ActionDelegate::SelectorPredicate selector_predicate_;
const Selector selector_;
WaitForDomOperation::Callback callback_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
std::set<const Script*> runnable_interrupts_;
bool element_check_result_ = false;
// An empty vector of interrupts that can be passed to interrupt_executor_
// and outlives it. Interrupts must not run interrupts.
const std::vector<Script*> no_interrupts_;
// The interrupt that's currently running.
std::unique_ptr<ScriptExecutor> interrupt_executor_;
// If true, pre-interrupt state was saved already. This happens just before
// the first interrupt.
bool saved_pre_interrupt_state_ = false;
// The status message that was displayed when the interrupt started.
std::string pre_interrupt_status_;
// Paths of the interrupts that were run during the current action.
std::set<std::string> ran_interrupts_;
RetryTimer retry_timer_;
base::WeakPtrFactory<WaitForDomOperation> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaitForDomOperation);
};
void OnGetActions(bool result, const std::string& response);
bool ProcessNextActionResponse(const std::string& response);
void ReportPayloadsToListener();
void ReportScriptsUpdateToListener(
std::vector<std::unique_ptr<Script>> scripts);
void RunCallback(bool success);
void RunCallbackWithResult(const Result& result);
void ProcessNextAction();
void ProcessAction(Action* action);
void GetNextActions();
void OnProcessedAction(std::unique_ptr<ProcessedActionProto> action);
void WaitForElement(base::TimeDelta max_wait_time,
const Selector& selectors,
base::OnceCallback<void(bool)> callback);
void CheckForElement(const Selector& selectors,
base::OnceCallback<void(bool)> callback);
void OnWaitForElementVisibleWithInterrupts(
base::OnceCallback<void(ProcessedActionStatusProto)> callback,
bool element_found,
const Result* interrupt_result,
const std::set<std::string>& ran_interrupts);
void OnWaitForElementVisibleNoInterrupts(
base::OnceCallback<void(ProcessedActionStatusProto)> callback,
bool element_found);
void OnGetPaymentInformation(
base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
std::unique_ptr<PaymentInformation> result);
void OnGetFullCard(GetFullCardCallback callback,
std::unique_ptr<autofill::CreditCard> card,
const base::string16& cvc);
void CleanUpAfterPrompt();
void OnChosen(base::OnceClosure callback);
std::string script_path_;
std::string last_global_payload_;
const std::string initial_script_payload_;
std::string last_script_payload_;
ScriptExecutor::Listener* const listener_;
ScriptExecutorDelegate* delegate_;
RunScriptCallback callback_;
std::vector<std::unique_ptr<Action>> actions_;
std::vector<ProcessedActionProto> processed_actions_;
AtEnd at_end_;
bool should_stop_script_;
bool should_clean_contextual_ui_on_finish_;
ActionProto::ActionInfoCase previous_action_type_;
Selector last_focused_element_selector_;
std::unique_ptr<ElementAreaProto> touchable_element_area_;
std::map<std::string, ScriptStatusProto>* scripts_state_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
// Paths of the interrupts that were run during the current script.
std::set<std::string> ran_interrupts_;
// Set of interrupts that might run during wait for dom actions with
// allow_interrupt. Sorted by priority; an interrupt that appears on the
// vector first should run first.
const std::vector<Script*>* ordered_interrupts_;
std::unique_ptr<WaitForDomOperation> wait_for_dom_;
// Callback set by Prompt(). This is called when the prompt is terminated
// without selecting any chips. nullptr unless showing a prompt.
base::OnceCallback<void()> on_terminate_prompt_;
RetryTimer retry_timer_;
base::WeakPtrFactory<ScriptExecutor> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ScriptExecutor);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_EXECUTOR_H_