blob: 27361f700d96d7445fbb810d62e303462297c083 [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.
#include "components/autofill_assistant/browser/actions/show_cast_action.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/actions/action_delegate_util.h"
#include "components/autofill_assistant/browser/client_settings.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
namespace autofill_assistant {
ShowCastAction::ShowCastAction(ActionDelegate* delegate,
const ActionProto& proto)
: Action(delegate, proto) {
DCHECK(proto_.has_show_cast());
}
ShowCastAction::~ShowCastAction() {}
void ShowCastAction::InternalProcessAction(ProcessActionCallback callback) {
process_action_callback_ = std::move(callback);
const ShowCastProto& show_cast = proto_.show_cast();
if (show_cast.has_title()) {
// TODO(crbug.com/806868): Deprecate and remove message from this action and
// use tell instead.
delegate_->SetStatusMessage(show_cast.title());
}
Selector selector = Selector(show_cast.element_to_present());
if (selector.empty()) {
VLOG(1) << __func__ << ": empty selector";
EndAction(ClientStatus(INVALID_SELECTOR));
return;
}
// Default value of 25%. This value should always be overridden by backend.
TopPadding top_padding{0.25, TopPadding::Unit::RATIO};
switch (show_cast.top_padding().top_padding_case()) {
case ShowCastProto::TopPadding::kPixels:
top_padding = TopPadding(show_cast.top_padding().pixels(),
TopPadding::Unit::PIXELS);
break;
case ShowCastProto::TopPadding::kRatio:
top_padding =
TopPadding(show_cast.top_padding().ratio(), TopPadding::Unit::RATIO);
break;
case ShowCastProto::TopPadding::TOP_PADDING_NOT_SET:
// Default value set before switch.
break;
}
delegate_->ShortWaitForElement(
selector, base::BindOnce(&ShowCastAction::OnWaitForElementTimed,
weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&ShowCastAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr(),
selector, top_padding)));
}
void ShowCastAction::OnWaitForElement(const Selector& selector,
const TopPadding& top_padding,
const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(element_status);
return;
}
if (proto_.show_cast().has_container()) {
Selector container_selector = Selector(proto_.show_cast().container());
if (container_selector.empty()) {
VLOG(1) << __func__ << ": empty selector for container";
EndAction(ClientStatus(INVALID_SELECTOR));
return;
}
delegate_->FindElement(
container_selector,
base::BindOnce(&ShowCastAction::OnFindContainer,
weak_ptr_factory_.GetWeakPtr(), selector, top_padding));
} else {
ScrollToElement(selector, top_padding, /* container= */ nullptr);
}
}
void ShowCastAction::OnFindContainer(
const Selector& selector,
const TopPadding& top_padding,
const ClientStatus& element_status,
std::unique_ptr<ElementFinder::Result> container) {
if (!element_status.ok()) {
VLOG(1) << __func__ << " Failed to find container.";
EndAction(element_status);
return;
}
ScrollToElement(selector, top_padding, std::move(container));
}
void ShowCastAction::ScrollToElement(
const Selector& selector,
const TopPadding& top_padding,
std::unique_ptr<ElementFinder::Result> container) {
auto actions = std::make_unique<action_delegate_util::ElementActionVector>();
actions->emplace_back(base::BindOnce(
&ShowCastAction::RunAndIncreaseWaitTimer, weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&ActionDelegate::WaitUntilDocumentIsInReadyState,
delegate_->GetWeakPtr(),
delegate_->GetSettings().document_ready_check_timeout,
DOCUMENT_INTERACTIVE)));
auto wait_for_stable_element = proto_.show_cast().wait_for_stable_element();
if (wait_for_stable_element == STEP_UNSPECIFIED) {
wait_for_stable_element = SKIP_STEP;
}
action_delegate_util::AddOptionalStep(
wait_for_stable_element,
base::BindOnce(&WebController::ScrollIntoView,
delegate_->GetWebController()->GetWeakPtr(), false),
actions.get());
action_delegate_util::AddOptionalStep(
wait_for_stable_element,
base::BindOnce(
&ShowCastAction::RunAndIncreaseWaitTimer,
weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&WebController::WaitUntilElementIsStable,
delegate_->GetWebController()->GetWeakPtr(),
proto_.show_cast().stable_check_max_rounds(),
base::TimeDelta::FromMilliseconds(
proto_.show_cast().stable_check_interval_ms()))),
actions.get());
actions->emplace_back(base::BindOnce(&ActionDelegate::ScrollToElementPosition,
delegate_->GetWeakPtr(), selector,
top_padding, std::move(container)));
action_delegate_util::FindElementAndPerform(
delegate_, selector,
base::BindOnce(&action_delegate_util::PerformAll, std::move(actions)),
base::BindOnce(&ShowCastAction::OnScrollToElementPosition,
weak_ptr_factory_.GetWeakPtr()));
}
void ShowCastAction::RunAndIncreaseWaitTimer(
base::OnceCallback<void(
const ElementFinder::Result&,
base::OnceCallback<void(const ClientStatus&, base::TimeDelta)>)> action,
const ElementFinder::Result& element,
base::OnceCallback<void(const ClientStatus&)> done) {
std::move(action).Run(
element, base::BindOnce(&ShowCastAction::OnWaitForElementTimed,
weak_ptr_factory_.GetWeakPtr(), std::move(done)));
}
void ShowCastAction::OnScrollToElementPosition(const ClientStatus& status) {
delegate_->SetTouchableElementArea(
proto().show_cast().touchable_element_area());
EndAction(status);
}
void ShowCastAction::EndAction(const ClientStatus& status) {
UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant