blob: bbb7092adc884ac845e15b35dbac83b4b185297f [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/autofill/autofill_keyboard_accessory_adapter.h"
#include <numeric>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
#include "components/autofill/core/browser/ui/popup_item_ids.h"
#include "components/autofill/core/browser/ui/suggestion.h"
namespace autofill {
constexpr char16_t kLabelSeparator = ' ';
constexpr size_t kMaxBulletCount = 8;
namespace {
std::u16string CreateLabel(const Suggestion& suggestion) {
std::u16string password =
suggestion.additional_label.substr(0, kMaxBulletCount);
// The label contains the signon_realm or is empty. The additional_label can
// never be empty since it must contain a password.
if (suggestion.label.empty())
return password;
return suggestion.label + kLabelSeparator + password;
}
} // namespace
AutofillKeyboardAccessoryAdapter::AutofillKeyboardAccessoryAdapter(
base::WeakPtr<AutofillPopupController> controller)
: controller_(controller) {}
AutofillKeyboardAccessoryAdapter::~AutofillKeyboardAccessoryAdapter() = default;
// AutofillPopupView implementation.
void AutofillKeyboardAccessoryAdapter::Show() {
DCHECK(view_) << "Show called before a View was set!";
OnSuggestionsChanged();
}
void AutofillKeyboardAccessoryAdapter::Hide() {
DCHECK(view_) << "Hide called before a View was set!";
view_->Hide();
}
void AutofillKeyboardAccessoryAdapter::OnSelectedRowChanged(
absl::optional<int> previous_row_selection,
absl::optional<int> current_row_selection) {}
void AutofillKeyboardAccessoryAdapter::OnSuggestionsChanged() {
DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
DCHECK(view_) << "OnSuggestionsChanged called before a View was set!";
labels_.clear();
front_element_ = absl::nullopt;
for (int i = 0; i < GetLineCount(); ++i) {
const Suggestion& suggestion = controller_->GetSuggestionAt(i);
if (suggestion.frontend_id != POPUP_ITEM_ID_CLEAR_FORM) {
labels_.push_back(CreateLabel(suggestion));
continue;
}
DCHECK(!front_element_.has_value()) << "Additional front item at: " << i;
front_element_ = absl::optional<int>(i);
// If there is a special popup item, just reuse the previously used label.
labels_.push_back(controller_->GetSuggestionLabelAt(i));
}
view_->Show();
}
absl::optional<int32_t> AutofillKeyboardAccessoryAdapter::GetAxUniqueId() {
NOTIMPLEMENTED() << "See https://crbug.com/985927";
return absl::nullopt;
}
// AutofillPopupController implementation.
void AutofillKeyboardAccessoryAdapter::AcceptSuggestion(int index) {
if (controller_)
controller_->AcceptSuggestion(OffsetIndexFor(index));
}
int AutofillKeyboardAccessoryAdapter::GetLineCount() const {
return controller_ ? controller_->GetLineCount() : 0;
}
const autofill::Suggestion& AutofillKeyboardAccessoryAdapter::GetSuggestionAt(
int row) const {
DCHECK(controller_) << "Call GetSuggestionAt only from its owner!";
return controller_->GetSuggestionAt(OffsetIndexFor(row));
}
std::u16string AutofillKeyboardAccessoryAdapter::GetSuggestionMainTextAt(
int row) const {
DCHECK(controller_) << "Call GetSuggestionMainTextAt only from its owner!";
return controller_->GetSuggestionMainTextAt(OffsetIndexFor(row));
}
std::u16string AutofillKeyboardAccessoryAdapter::GetSuggestionMinorTextAt(
int row) const {
DCHECK(controller_) << "Call GetSuggestionMinorTextAt only from its owner!";
return controller_->GetSuggestionMinorTextAt(OffsetIndexFor(row));
}
const std::u16string& AutofillKeyboardAccessoryAdapter::GetSuggestionLabelAt(
int row) const {
DCHECK(controller_) << "Call GetSuggestionLabelAt only from its owner!";
DCHECK(static_cast<size_t>(row) < labels_.size());
return labels_[OffsetIndexFor(row)];
}
PopupType AutofillKeyboardAccessoryAdapter::GetPopupType() const {
DCHECK(controller_) << "Call GetPopupType only from its owner!";
return controller_->GetPopupType();
}
bool AutofillKeyboardAccessoryAdapter::GetRemovalConfirmationText(
int index,
std::u16string* title,
std::u16string* body) {
return controller_ && controller_->GetRemovalConfirmationText(
OffsetIndexFor(index), title, body);
}
bool AutofillKeyboardAccessoryAdapter::RemoveSuggestion(int index) {
DCHECK(view_) << "RemoveSuggestion called before a View was set!";
std::u16string title, body;
if (!GetRemovalConfirmationText(index, &title, &body))
return false;
view_->ConfirmDeletion(
title, body,
base::BindOnce(&AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed,
weak_ptr_factory_.GetWeakPtr(), index));
return true;
}
void AutofillKeyboardAccessoryAdapter::SetSelectedLine(
absl::optional<int> selected_line) {
if (!controller_)
return;
if (!selected_line.has_value()) {
controller_->SetSelectedLine(absl::nullopt);
return;
}
controller_->SetSelectedLine(OffsetIndexFor(selected_line.value()));
}
absl::optional<int> AutofillKeyboardAccessoryAdapter::selected_line() const {
if (!controller_ || !controller_->selected_line().has_value())
return absl::nullopt;
for (int i = 0; i < GetLineCount(); ++i) {
if (OffsetIndexFor(i) == controller_->selected_line().value()) {
return i;
}
}
return absl::nullopt;
}
// AutofillPopupViewDelegate implementation
void AutofillKeyboardAccessoryAdapter::Hide(PopupHidingReason reason) {
if (controller_)
controller_->Hide(reason);
}
void AutofillKeyboardAccessoryAdapter::ViewDestroyed() {
if (controller_)
controller_->ViewDestroyed();
view_.reset();
// The controller has now deleted itself.
controller_ = nullptr;
delete this; // Remove dangling weak reference.
}
void AutofillKeyboardAccessoryAdapter::SelectionCleared() {
if (controller_)
controller_->SelectionCleared();
}
gfx::NativeView AutofillKeyboardAccessoryAdapter::container_view() const {
DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
return controller_->container_view();
}
content::WebContents* AutofillKeyboardAccessoryAdapter::GetWebContents() const {
return controller_->GetWebContents();
}
const gfx::RectF& AutofillKeyboardAccessoryAdapter::element_bounds() const {
DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
return controller_->element_bounds();
}
bool AutofillKeyboardAccessoryAdapter::IsRTL() const {
return controller_ && controller_->IsRTL();
}
std::vector<Suggestion> AutofillKeyboardAccessoryAdapter::GetSuggestions()
const {
if (!controller_)
return std::vector<Suggestion>();
std::vector<Suggestion> suggestions = controller_->GetSuggestions();
if (front_element_.has_value()) {
std::rotate(suggestions.begin(),
suggestions.begin() + front_element_.value(),
suggestions.begin() + front_element_.value() + 1);
}
return suggestions;
}
void AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed(int index) {
if (controller_)
controller_->RemoveSuggestion(OffsetIndexFor(index));
}
int AutofillKeyboardAccessoryAdapter::OffsetIndexFor(int element_index) const {
if (!front_element_.has_value())
return element_index;
if (0 == element_index)
return front_element_.value();
return element_index - (element_index <= front_element_.value() ? 1 : 0);
}
} // namespace autofill