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