blob: f3a83b4874b2d1dbd499798515cdb9dc179f58cb [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/prompt_action.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "url/gurl.h"
namespace autofill_assistant {
PromptAction::PromptAction(const ActionProto& proto)
: Action(proto), weak_ptr_factory_(this) {
DCHECK(proto_.has_prompt());
}
PromptAction::~PromptAction() {}
void PromptAction::InternalProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) {
delegate->SetStatusMessage(proto_.prompt().message());
callback_ = std::move(callback);
DCHECK_GT(proto_.prompt().choices_size(), 0);
delegate->Prompt(CreateChips());
batch_element_checker_ = delegate->CreateBatchElementChecker();
// Register elements whose existence enable new chips.
for (int i = 0; i < proto_.prompt().choices_size(); i++) {
auto& choice_proto = proto_.prompt().choices(i);
Selector selector(choice_proto.show_only_if_element_exists());
if (selector.empty())
continue;
batch_element_checker_->AddElementCheck(
kExistenceCheck, selector,
base::BindOnce(&PromptAction::OnRequiredElementExists,
weak_ptr_factory_.GetWeakPtr(),
base::Unretained(delegate), i));
}
// Wait as long as necessary for one of the elements to show up. This is
// cancelled by CancelProto()
for (const auto& choice_proto : proto_.prompt().choices()) {
Selector selector(choice_proto.element_exists());
if (selector.empty())
continue;
std::string payload;
choice_proto.SerializeToString(&payload);
batch_element_checker_->AddElementCheck(
kExistenceCheck, selector,
base::BindOnce(&PromptAction::OnElementExist,
weak_ptr_factory_.GetWeakPtr(), payload));
}
batch_element_checker_->Run(
base::TimeDelta::Max(),
/* try_done= */
base::BindRepeating(&PromptAction::OnElementChecksDone,
weak_ptr_factory_.GetWeakPtr(),
base::Unretained(delegate)),
/* all_done= */ base::DoNothing());
}
std::unique_ptr<std::vector<Chip>> PromptAction::CreateChips() {
auto chips = std::make_unique<std::vector<Chip>>();
// TODO(crbug.com/806868): Surface type in proto instead of guessing it from
// highlight flag.
Chip::Type non_highlight_type = Chip::Type::CHIP_ASSISTIVE;
for (const auto& choice_proto : proto_.prompt().choices()) {
if (!choice_proto.name().empty() && choice_proto.highlight()) {
non_highlight_type = Chip::Type::BUTTON_TEXT;
break;
}
}
for (int i = 0; i < proto_.prompt().choices_size(); i++) {
auto& choice_proto = proto_.prompt().choices(i);
if (choice_proto.show_only_if_element_exists().selectors_size() > 0 &&
required_element_found_.count(i) == 0)
continue;
chips->emplace_back();
chips->back().type = choice_proto.highlight()
? Chip::Type::BUTTON_FILLED_BLUE
: non_highlight_type;
chips->back().text = choice_proto.name();
std::string server_payload;
choice_proto.SerializeToString(&server_payload);
chips->back().callback =
base::BindOnce(&PromptAction::OnSuggestionChosen,
weak_ptr_factory_.GetWeakPtr(), server_payload);
}
return chips;
}
void PromptAction::OnElementExist(const std::string& payload, bool exists) {
if (exists)
forced_payload_ = payload;
// Forcing the chip click is delayed until try_done, as it indirectly deletes
// batch_element_checker_, which isn't supported from an element check
// callback.
}
void PromptAction::OnRequiredElementExists(ActionDelegate* delegate,
int choice_index,
bool exists) {
if (!exists)
return;
required_element_found_.insert(choice_index);
delegate->Prompt(CreateChips());
}
void PromptAction::OnElementChecksDone(ActionDelegate* delegate) {
if (!forced_payload_.empty()) {
delegate->CancelPrompt();
OnSuggestionChosen(forced_payload_);
}
}
void PromptAction::OnSuggestionChosen(const std::string& payload) {
batch_element_checker_.reset();
PromptProto::Choice choice;
if (!choice.ParseFromString(payload)) {
DLOG(ERROR) << "Invalid result.";
UpdateProcessedAction(OTHER_ACTION_STATUS);
} else {
UpdateProcessedAction(ACTION_APPLIED);
std::swap(*processed_action_proto_->mutable_prompt_choice(), choice);
}
std::move(callback_).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant