blob: 3c85c82e2e68fc2e084730f1687024545a561d6c [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_WEB_WEB_CONTROLLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_WEB_CONTROLLER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/autofill_assistant/browser/action_value.pb.h"
#include "components/autofill_assistant/browser/batch_element_checker.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_dom.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_input.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_network.h"
#include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
#include "components/autofill_assistant/browser/devtools/devtools_client.h"
#include "components/autofill_assistant/browser/rectf.h"
#include "components/autofill_assistant/browser/selector.h"
#include "components/autofill_assistant/browser/top_padding.h"
#include "components/autofill_assistant/browser/web/check_on_top_worker.h"
#include "components/autofill_assistant/browser/web/click_or_tap_worker.h"
#include "components/autofill_assistant/browser/web/element_finder.h"
#include "components/autofill_assistant/browser/web/element_position_getter.h"
#include "components/autofill_assistant/browser/web/element_rect_getter.h"
#include "components/autofill_assistant/browser/web/send_keyboard_input_worker.h"
#include "components/autofill_assistant/browser/web/web_controller_worker.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/icu/source/common/unicode/umachine.h"
#include "url/gurl.h"
namespace autofill {
class AutofillProfile;
class ContentAutofillDriver;
class CreditCard;
struct FormData;
struct FormFieldData;
} // namespace autofill
namespace content {
class WebContents;
class RenderFrameHost;
} // namespace content
namespace autofill_assistant {
// Controller to interact with the web pages.
//
// WARNING: Accessing or modifying page elements must be run in sequence: wait
// until the result of the first operation has been given to the callback before
// starting a new operation.
//
// TODO(crbug.com/806868): Figure out the reason for this limitation and fix it.
// Also, consider structuring the WebController to make it easier to run
// multiple operations, whether in sequence or in parallel.
class WebController {
public:
// Create web controller for a given |web_contents|. |settings| must be valid
// for the lifetime of the controller.
static std::unique_ptr<WebController> CreateForWebContents(
content::WebContents* web_contents);
// |web_contents| and |settings| must outlive this web controller.
WebController(content::WebContents* web_contents,
std::unique_ptr<DevtoolsClient> devtools_client);
virtual ~WebController();
// Load |url| in the current tab. Returns immediately, before the new page has
// been loaded.
virtual void LoadURL(const GURL& url);
// Find the element given by |selector|. If multiple elements match
// |selector| and if |strict_mode| is false, return the first one that is
// found. Otherwise if |strict-mode| is true, do not return any.
//
// To check multiple elements, use a BatchElementChecker.
virtual void FindElement(const Selector& selector,
bool strict_mode,
ElementFinder::Callback callback);
// Find all elements matching |selector|. If there are no matches, the status
// will be ELEMENT_RESOLUTION_FAILED.
virtual void FindAllElements(const Selector& selector,
ElementFinder::Callback callback);
// Scroll the |element| into view if needed, center the element on the screen
// if specified.
virtual void ScrollIntoView(
bool center,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Send a JS click to the |element|.
virtual void JsClickElement(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Perform a mouse left button click or a touch tap on the |element|
// return the result through callback.
virtual void ClickOrTapElement(
ClickType click_type,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Get a stable position of the given element. Fail with ELEMENT_UNSTABLE if
// the element position doesn't stabilize quickly enough.
virtual void WaitUntilElementIsStable(
int max_rounds,
base::TimeDelta check_interval,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, base::TimeDelta)> callback);
// Check whether the center given element is on top. Fail with
// ELEMENT_NOT_ON_TOP if the center of the element is covered.
virtual void CheckOnTop(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Fill the address form given by |selector| with the given address
// |profile|.
virtual void FillAddressForm(
const autofill::AutofillProfile* profile,
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback);
// Fill the card form given by |selector| with the given |card| and its
// |cvc|.
virtual void FillCardForm(
std::unique_ptr<autofill::CreditCard> card,
const std::u16string& cvc,
const Selector& selector,
base::OnceCallback<void(const ClientStatus&)> callback);
// Return |FormData| and |FormFieldData| for the element identified with
// |selector|. The result is returned asynchronously through |callback|.
virtual void RetrieveElementFormAndFieldData(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&,
const autofill::FormData& form_data,
const autofill::FormFieldData& field_data)>
callback);
// Select the option to be picked given by the |re2| in the |element|.
virtual void SelectOption(
const std::string& re2,
bool case_sensitive,
SelectOptionProto::OptionComparisonAttribute option_comparison_attribute,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Set the selected |option| of the |element|.
virtual void SelectOptionElement(
const ElementFinder::Result& option,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Check if the selected option of the |element| is the expected |option|.
virtual void CheckSelectedOptionElement(
const ElementFinder::Result& option,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Highlight an |element|.
virtual void HighlightElement(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Scrolls |container| to an |element|'s position. |top_padding|
// specifies the padding between the focused element and the top of the
// container. If |scrollable_element| is not specified, the window will be
// scrolled instead.
virtual void ScrollToElementPosition(
std::unique_ptr<ElementFinder::Result> container,
const ElementFinder::Result& element,
const TopPadding& top_padding,
base::OnceCallback<void(const ClientStatus&)> callback);
// Get the value attribute of an |element| and return the result through
// |callback|. If the lookup fails, the value will be empty. An empty result
// does not mean an error.
//
// Normally done through BatchElementChecker.
virtual void GetFieldValue(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback);
// Get the value of a nested |attribute| from an |element| and return the
// result through |callback|. If the lookup fails, the value will be empty.
// An empty result does not mean an error.
virtual void GetStringAttribute(
const std::vector<std::string>& attributes,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback);
// Set the value attribute of an |element| to the specified |value| and
// trigger an onchange event.
virtual void SetValueAttribute(
const std::string& value,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Set the nested |attributes| of an |element| to the specified |value|.
virtual void SetAttribute(
const std::vector<std::string>& attributes,
const std::string& value,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Select the current value in a text |element|.
virtual void SelectFieldValue(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Focus the current |element|.
virtual void FocusField(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Inputs the specified codepoints into |element|. Expects the |element| to
// have focus. Key presses will have a delay of
// |key_press_delay_in_millisecond| between them. Returns the result through
// |callback|.
virtual void SendKeyboardInput(
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Inputs the specified |value| into |element| with keystrokes per character.
// Expects the |element| to have focus. Key presses will have a delay of
// |key_press_delay_in_millisecond| between them. Returns the result through
// |callback|.
virtual void SendTextInput(
int key_press_delay_in_millisecond,
const std::string& value,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Sends the specified key event. Expects |element| to have focus.
virtual void SendKeyEvent(
const KeyEvent& key_event,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Return the outerHTML of |element|.
virtual void GetOuterHtml(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback);
// Return the outerHTML of each element in |elements|. |elements| must contain
// the object ID of a JS array containing the elements.
virtual void GetOuterHtmls(
const ElementFinder::Result& elements,
base::OnceCallback<void(const ClientStatus&,
const std::vector<std::string>&)> callback);
// Return the tag of the |element|. In case of an error, will return an empty
// string.
virtual void GetElementTag(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback);
// Gets the visual viewport coordinates and size.
//
// The rectangle is expressed in absolute CSS coordinates.
virtual void GetVisualViewport(
base::OnceCallback<void(const ClientStatus&, const RectF&)> callback);
// Gets the position of the |element|.
//
// If unsuccessful, the callback gets the failure status with an empty rect.
//
// If successful, the callback gets a success status with a set of
// (left, top, right, bottom) coordinates rect, expressed in absolute CSS
// coordinates.
virtual void GetElementRect(const ElementFinder::Result& element,
ElementRectGetter::ElementRectCallback callback);
// Calls the callback once the main document window has been resized.
virtual void WaitForWindowHeightChange(
base::OnceCallback<void(const ClientStatus&)> callback);
// Gets the value of document.readyState for |optional_frame_element| or, if
// it is empty, in the main document.
virtual void GetDocumentReadyState(
const ElementFinder::Result& optional_frame_element,
base::OnceCallback<void(const ClientStatus&, DocumentReadyState)>
callback);
// Waits for the value of Document.readyState to satisfy |min_ready_state| in
// |optional_frame_element| or, if it is empty, in the main document.
virtual void WaitForDocumentReadyState(
const ElementFinder::Result& optional_frame_element,
DocumentReadyState min_ready_state,
base::OnceCallback<void(const ClientStatus&,
DocumentReadyState,
base::TimeDelta)> callback);
// Trigger a "change" event on the |element|.
virtual void SendChangeEvent(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> callback);
// Dispatch a custom JS event 'duplexweb' with an optional payload.
virtual void DispatchJsEvent(
base::OnceCallback<void(const ClientStatus&)> callback) const;
virtual base::WeakPtr<WebController> GetWeakPtr() const;
private:
friend class WebControllerBrowserTest;
struct FillFormInputData {
FillFormInputData();
~FillFormInputData();
// Data for filling address form.
std::unique_ptr<autofill::AutofillProfile> profile;
// Data for filling card form.
std::unique_ptr<autofill::CreditCard> card;
std::u16string cvc;
};
// RAII object that sets the action state to "running" when the object is
// allocated and to "not running" when it gets deallocated.
class ScopedAssistantActionStateRunning
: private content::WebContentsObserver {
public:
explicit ScopedAssistantActionStateRunning(
content::WebContents* web_contents,
content::RenderFrameHost* render_frame_host);
~ScopedAssistantActionStateRunning() override;
ScopedAssistantActionStateRunning(
const ScopedAssistantActionStateRunning&) = delete;
ScopedAssistantActionStateRunning& operator=(
const ScopedAssistantActionStateRunning&) = delete;
private:
void SetAssistantActionState(bool running);
// Overrides content::WebContentsObserver:
void RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) override;
content::RenderFrameHost* render_frame_host_;
};
void OnJavaScriptResult(
base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnJavaScriptResultForInt(
base::OnceCallback<void(const ClientStatus&, int)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnJavaScriptResultForString(
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnJavaScriptResultForStringArray(
base::OnceCallback<void(const ClientStatus&,
const std::vector<std::string>&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnCheckOnTop(CheckOnTopWorker* worker,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status);
void OnWaitUntilElementIsStable(
ElementPositionGetter* getter_to_release,
base::TimeTicks wait_time_start,
base::OnceCallback<void(const ClientStatus&, base::TimeDelta)> callback,
const ClientStatus& status);
void OnClickOrTapElement(
ClickOrTapWorker* getter_to_release,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status);
void OnWaitForWindowHeightChange(
base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result);
void RunElementFinder(const Selector& selector,
ElementFinder::ResultType result_type,
ElementFinder::Callback callback);
void OnFindElementResult(ElementFinder* finder_to_release,
ElementFinder::Callback callback,
const ClientStatus& status,
std::unique_ptr<ElementFinder::Result> result);
void GetElementFormAndFieldData(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&,
autofill::ContentAutofillDriver* driver,
const autofill::FormData&,
const autofill::FormFieldData&)> callback);
void OnFindElementForGetFormAndFieldData(
const Selector& selector,
base::OnceCallback<void(const ClientStatus&,
autofill::ContentAutofillDriver* driver,
const autofill::FormData&,
const autofill::FormFieldData&)> callback,
const ClientStatus& element_status,
std::unique_ptr<ElementFinder::Result> element_result);
void GetUniqueElementSelector(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&, int)>
callback);
void OnGetElementTagForUniqueSelector(
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, const std::string&, int)>
callback,
const ClientStatus& tag_status,
const std::string& tag);
void GetElementQueryIndex(
const std::string& query_selector,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&, int)> callback);
void OnGetElementQueryIndexForUniqueSelector(
base::OnceCallback<void(const ClientStatus&, const std::string&, int)>
callback,
const std::string& query_selector,
const ClientStatus& index_status,
int index);
void OnGetUniqueSelectorForFormAndFieldData(
base::OnceCallback<void(const ClientStatus&,
autofill::ContentAutofillDriver* driver,
const autofill::FormData&,
const autofill::FormFieldData&)> callback,
std::unique_ptr<ElementFinder::Result> element,
const ClientStatus& selector_status,
const std::string& query_selector,
int index);
void OnGetFormAndFieldData(
base::OnceCallback<void(const ClientStatus&,
autofill::ContentAutofillDriver* driver,
const autofill::FormData&,
const autofill::FormFieldData&)> callback,
autofill::ContentAutofillDriver* driver,
const autofill::FormData& form_data,
const autofill::FormFieldData& form_field);
void OnGetFormAndFieldDataForFilling(
std::unique_ptr<FillFormInputData> data_to_autofill,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& form_status,
autofill::ContentAutofillDriver* driver,
const autofill::FormData& form_data,
const autofill::FormFieldData& form_field);
void OnGetFormAndFieldDataForRetrieving(
base::OnceCallback<void(const ClientStatus&,
const autofill::FormData& form_data,
const autofill::FormFieldData& field_data)>
callback,
const ClientStatus& form_status,
autofill::ContentAutofillDriver* driver,
const autofill::FormData& form_data,
const autofill::FormFieldData& form_field);
void OnJavascriptResultExpectingTrue(
base::OnceCallback<void(const ClientStatus&)> callback,
ProcessedActionStatusProto status_if_false,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnSendKeyboardInputDone(
SendKeyboardInputWorker* worker_to_release,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status);
void OnGetElementRect(ElementRectGetter* getter_to_release,
ElementRectGetter::ElementRectCallback callback,
const ClientStatus& rect_status,
const RectF& element_rect);
void OnGetVisualViewport(
base::OnceCallback<void(const ClientStatus&, const RectF&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result);
void OnWaitForDocumentReadyState(
base::OnceCallback<void(const ClientStatus&,
DocumentReadyState,
base::TimeDelta)> callback,
base::TimeTicks wait_start_time,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result);
void OnDispatchJsEvent(base::OnceCallback<void(const ClientStatus&)> callback,
const DevtoolsClient::ReplyStatus& reply_status,
std::unique_ptr<runtime::EvaluateResult> result) const;
// Wrapper for calling the |callback| after re-enabling the keyboard by
// setting the assistant action state to "not running".
void RetainAssistantActionRunningStateAndExecuteCallback(
std::unique_ptr<ScopedAssistantActionStateRunning> scoped_state,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& client_status);
// Disables the keyboard by setting the assistant action state to "running"
// and wraps the |callback| such that the keyboard is re-enabled before
// calling it. Uses the |RenderFrameHost| of the |ElementFinder::Result| to
// extract the appropriate |ContentAutofillDriver|.
base::OnceCallback<void(const ClientStatus&)>
GetAssistantActionRunningStateRetainingCallback(
const ElementFinder::Result& element_result,
base::OnceCallback<void(const ClientStatus&)> callback);
// Weak pointer is fine here since it must outlive this web controller, which
// is guaranteed by the owner of this object.
content::WebContents* web_contents_;
std::unique_ptr<DevtoolsClient> devtools_client_;
// Currently running workers.
std::vector<std::unique_ptr<WebControllerWorker>> pending_workers_;
base::WeakPtrFactory<WebController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WebController);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_WEB_CONTROLLER_H_