1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/autofill/autofill_keyboard_accessory_adapter.h"
6
7 #include <numeric>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
15 #include "components/autofill/core/browser/ui/popup_item_ids.h"
16 #include "components/autofill/core/browser/ui/suggestion.h"
17
18 namespace autofill {
19
20 constexpr base::char16 kLabelSeparator = ' ';
21 constexpr size_t kMaxBulletCount = 8;
22
23 namespace {
CreateLabel(const Suggestion & suggestion)24 base::string16 CreateLabel(const Suggestion& suggestion) {
25 base::string16 password =
26 suggestion.additional_label.substr(0, kMaxBulletCount);
27 // The label contains the signon_realm or is empty. The additional_label can
28 // never be empty since it must contain a password.
29 if (suggestion.label.empty())
30 return password;
31 return suggestion.label + kLabelSeparator + password;
32 }
33
34 } // namespace
35
AutofillKeyboardAccessoryAdapter(base::WeakPtr<AutofillPopupController> controller)36 AutofillKeyboardAccessoryAdapter::AutofillKeyboardAccessoryAdapter(
37 base::WeakPtr<AutofillPopupController> controller)
38 : controller_(controller) {}
39
40 AutofillKeyboardAccessoryAdapter::~AutofillKeyboardAccessoryAdapter() = default;
41
42 // AutofillPopupView implementation.
43
Show()44 void AutofillKeyboardAccessoryAdapter::Show() {
45 DCHECK(view_) << "Show called before a View was set!";
46 OnSuggestionsChanged();
47 }
48
Hide()49 void AutofillKeyboardAccessoryAdapter::Hide() {
50 DCHECK(view_) << "Hide called before a View was set!";
51 view_->Hide();
52 }
53
OnSelectedRowChanged(base::Optional<int> previous_row_selection,base::Optional<int> current_row_selection)54 void AutofillKeyboardAccessoryAdapter::OnSelectedRowChanged(
55 base::Optional<int> previous_row_selection,
56 base::Optional<int> current_row_selection) {}
57
OnSuggestionsChanged()58 void AutofillKeyboardAccessoryAdapter::OnSuggestionsChanged() {
59 DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
60 DCHECK(view_) << "OnSuggestionsChanged called before a View was set!";
61
62 labels_.clear();
63 front_element_ = base::nullopt;
64 for (int i = 0; i < GetLineCount(); ++i) {
65 const Suggestion& suggestion = controller_->GetSuggestionAt(i);
66 if (suggestion.frontend_id != POPUP_ITEM_ID_CLEAR_FORM) {
67 labels_.push_back(CreateLabel(suggestion));
68 continue;
69 }
70 DCHECK(!front_element_.has_value()) << "Additional front item at: " << i;
71 front_element_ = base::Optional<int>(i);
72 // If there is a special popup item, just reuse the previously used label.
73 labels_.push_back(controller_->GetSuggestionLabelAt(i));
74 }
75
76 view_->Show();
77 }
78
GetAxUniqueId()79 base::Optional<int32_t> AutofillKeyboardAccessoryAdapter::GetAxUniqueId() {
80 NOTIMPLEMENTED() << "See https://crbug.com/985927";
81 return base::nullopt;
82 }
83
84 // AutofillPopupController implementation.
85
AcceptSuggestion(int index)86 void AutofillKeyboardAccessoryAdapter::AcceptSuggestion(int index) {
87 if (controller_)
88 controller_->AcceptSuggestion(OffsetIndexFor(index));
89 }
90
GetLineCount() const91 int AutofillKeyboardAccessoryAdapter::GetLineCount() const {
92 return controller_ ? controller_->GetLineCount() : 0;
93 }
94
GetSuggestionAt(int row) const95 const autofill::Suggestion& AutofillKeyboardAccessoryAdapter::GetSuggestionAt(
96 int row) const {
97 DCHECK(controller_) << "Call GetSuggestionAt only from its owner!";
98 return controller_->GetSuggestionAt(OffsetIndexFor(row));
99 }
100
GetSuggestionValueAt(int row) const101 const base::string16& AutofillKeyboardAccessoryAdapter::GetSuggestionValueAt(
102 int row) const {
103 DCHECK(controller_) << "Call GetSuggestionValueAt only from its owner!";
104 return controller_->GetSuggestionValueAt(OffsetIndexFor(row));
105 }
106
GetSuggestionLabelAt(int row) const107 const base::string16& AutofillKeyboardAccessoryAdapter::GetSuggestionLabelAt(
108 int row) const {
109 DCHECK(controller_) << "Call GetSuggestionLabelAt only from its owner!";
110 DCHECK(static_cast<size_t>(row) < labels_.size());
111 return labels_[OffsetIndexFor(row)];
112 }
113
GetPopupType() const114 PopupType AutofillKeyboardAccessoryAdapter::GetPopupType() const {
115 DCHECK(controller_) << "Call GetPopupType only from its owner!";
116 return controller_->GetPopupType();
117 }
118
GetRemovalConfirmationText(int index,base::string16 * title,base::string16 * body)119 bool AutofillKeyboardAccessoryAdapter::GetRemovalConfirmationText(
120 int index,
121 base::string16* title,
122 base::string16* body) {
123 return controller_ && controller_->GetRemovalConfirmationText(
124 OffsetIndexFor(index), title, body);
125 }
126
RemoveSuggestion(int index)127 bool AutofillKeyboardAccessoryAdapter::RemoveSuggestion(int index) {
128 DCHECK(view_) << "RemoveSuggestion called before a View was set!";
129 base::string16 title, body;
130 if (!GetRemovalConfirmationText(index, &title, &body))
131 return false;
132
133 view_->ConfirmDeletion(
134 title, body,
135 base::BindOnce(&AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed,
136 weak_ptr_factory_.GetWeakPtr(), index));
137 return true;
138 }
139
SetSelectedLine(base::Optional<int> selected_line)140 void AutofillKeyboardAccessoryAdapter::SetSelectedLine(
141 base::Optional<int> selected_line) {
142 if (!controller_)
143 return;
144 if (!selected_line.has_value()) {
145 controller_->SetSelectedLine(base::nullopt);
146 return;
147 }
148 controller_->SetSelectedLine(OffsetIndexFor(selected_line.value()));
149 }
150
selected_line() const151 base::Optional<int> AutofillKeyboardAccessoryAdapter::selected_line() const {
152 if (!controller_ || !controller_->selected_line().has_value())
153 return base::nullopt;
154 for (int i = 0; i < GetLineCount(); ++i) {
155 if (OffsetIndexFor(i) == controller_->selected_line().value()) {
156 return i;
157 }
158 }
159 return base::nullopt;
160 }
161
162 // AutofillPopupViewDelegate implementation
163
Hide(PopupHidingReason reason)164 void AutofillKeyboardAccessoryAdapter::Hide(PopupHidingReason reason) {
165 if (controller_)
166 controller_->Hide(reason);
167 }
168
ViewDestroyed()169 void AutofillKeyboardAccessoryAdapter::ViewDestroyed() {
170 if (controller_)
171 controller_->ViewDestroyed();
172
173 view_.reset();
174
175 // The controller has now deleted itself.
176 controller_ = nullptr;
177 delete this; // Remove dangling weak reference.
178 }
179
SelectionCleared()180 void AutofillKeyboardAccessoryAdapter::SelectionCleared() {
181 if (controller_)
182 controller_->SelectionCleared();
183 }
184
container_view() const185 gfx::NativeView AutofillKeyboardAccessoryAdapter::container_view() const {
186 DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
187 return controller_->container_view();
188 }
189
GetWebContents() const190 content::WebContents* AutofillKeyboardAccessoryAdapter::GetWebContents() const {
191 return controller_->GetWebContents();
192 }
193
element_bounds() const194 const gfx::RectF& AutofillKeyboardAccessoryAdapter::element_bounds() const {
195 DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!";
196 return controller_->element_bounds();
197 }
198
IsRTL() const199 bool AutofillKeyboardAccessoryAdapter::IsRTL() const {
200 return controller_ && controller_->IsRTL();
201 }
202
GetSuggestions() const203 std::vector<Suggestion> AutofillKeyboardAccessoryAdapter::GetSuggestions()
204 const {
205 if (!controller_)
206 return std::vector<Suggestion>();
207 std::vector<Suggestion> suggestions = controller_->GetSuggestions();
208 if (front_element_.has_value()) {
209 std::rotate(suggestions.begin(),
210 suggestions.begin() + front_element_.value(),
211 suggestions.begin() + front_element_.value() + 1);
212 }
213 return suggestions;
214 }
215
OnDeletionConfirmed(int index)216 void AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed(int index) {
217 if (controller_)
218 controller_->RemoveSuggestion(OffsetIndexFor(index));
219 }
220
OffsetIndexFor(int element_index) const221 int AutofillKeyboardAccessoryAdapter::OffsetIndexFor(int element_index) const {
222 if (!front_element_.has_value())
223 return element_index;
224 if (0 == element_index)
225 return front_element_.value();
226 return element_index - (element_index <= front_element_.value() ? 1 : 0);
227 }
228
229 } // namespace autofill
230