• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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/ui/autofill/autofill_popup_controller_impl.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/ui/autofill/autofill_popup_view.h"
13 #include "components/autofill/core/browser/autofill_popup_delegate.h"
14 #include "content/public/browser/native_web_keyboard_event.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "grit/webkit_resources.h"
18 #include "third_party/WebKit/public/web/WebAutofillClient.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/events/event.h"
21 #include "ui/gfx/display.h"
22 #include "ui/gfx/rect_conversions.h"
23 #include "ui/gfx/screen.h"
24 #include "ui/gfx/text_elider.h"
25 #include "ui/gfx/vector2d.h"
26 
27 using base::WeakPtr;
28 using blink::WebAutofillClient;
29 
30 namespace autofill {
31 namespace {
32 
33 // Used to indicate that no line is currently selected by the user.
34 const int kNoSelection = -1;
35 
36 // The vertical height of each row in pixels.
37 const size_t kRowHeight = 24;
38 
39 // The vertical height of a separator in pixels.
40 const size_t kSeparatorHeight = 1;
41 
42 #if !defined(OS_ANDROID)
43 // Size difference between name and subtext in pixels.
44 const int kLabelFontSizeDelta = -2;
45 
46 const size_t kNamePadding = AutofillPopupView::kNamePadding;
47 const size_t kIconPadding = AutofillPopupView::kIconPadding;
48 const size_t kEndPadding = AutofillPopupView::kEndPadding;
49 #endif
50 
51 struct DataResource {
52   const char* name;
53   int id;
54 };
55 
56 const DataResource kDataResources[] = {
57   { "americanExpressCC", IDR_AUTOFILL_CC_AMEX },
58   { "dinersCC", IDR_AUTOFILL_CC_DINERS },
59   { "discoverCC", IDR_AUTOFILL_CC_DISCOVER },
60   { "genericCC", IDR_AUTOFILL_CC_GENERIC },
61   { "jcbCC", IDR_AUTOFILL_CC_JCB },
62   { "masterCardCC", IDR_AUTOFILL_CC_MASTERCARD },
63   { "visaCC", IDR_AUTOFILL_CC_VISA },
64 };
65 
66 }  // namespace
67 
68 // static
GetOrCreate(WeakPtr<AutofillPopupControllerImpl> previous,WeakPtr<AutofillPopupDelegate> delegate,content::WebContents * web_contents,gfx::NativeView container_view,const gfx::RectF & element_bounds,base::i18n::TextDirection text_direction)69 WeakPtr<AutofillPopupControllerImpl> AutofillPopupControllerImpl::GetOrCreate(
70     WeakPtr<AutofillPopupControllerImpl> previous,
71     WeakPtr<AutofillPopupDelegate> delegate,
72     content::WebContents* web_contents,
73     gfx::NativeView container_view,
74     const gfx::RectF& element_bounds,
75     base::i18n::TextDirection text_direction) {
76   DCHECK(!previous.get() || previous->delegate_.get() == delegate.get());
77 
78   if (previous.get() && previous->web_contents_ == web_contents &&
79       previous->container_view() == container_view &&
80       previous->element_bounds() == element_bounds) {
81     previous->ClearState();
82     return previous;
83   }
84 
85   if (previous.get())
86     previous->Hide();
87 
88   AutofillPopupControllerImpl* controller =
89       new AutofillPopupControllerImpl(
90           delegate, web_contents, container_view, element_bounds,
91           text_direction);
92   return controller->GetWeakPtr();
93 }
94 
AutofillPopupControllerImpl(base::WeakPtr<AutofillPopupDelegate> delegate,content::WebContents * web_contents,gfx::NativeView container_view,const gfx::RectF & element_bounds,base::i18n::TextDirection text_direction)95 AutofillPopupControllerImpl::AutofillPopupControllerImpl(
96     base::WeakPtr<AutofillPopupDelegate> delegate,
97     content::WebContents* web_contents,
98     gfx::NativeView container_view,
99     const gfx::RectF& element_bounds,
100     base::i18n::TextDirection text_direction)
101     : view_(NULL),
102       delegate_(delegate),
103       web_contents_(web_contents),
104       container_view_(container_view),
105       element_bounds_(element_bounds),
106       text_direction_(text_direction),
107       registered_key_press_event_callback_with_(NULL),
108       hide_on_outside_click_(false),
109       key_press_event_callback_(
110           base::Bind(&AutofillPopupControllerImpl::HandleKeyPressEvent,
111                      base::Unretained(this))),
112       weak_ptr_factory_(this) {
113   ClearState();
114 #if !defined(OS_ANDROID)
115   subtext_font_ = name_font_.DeriveFont(kLabelFontSizeDelta);
116 #if defined(OS_MACOSX)
117   // There is no italic version of the system font.
118   warning_font_ = name_font_;
119 #else
120   warning_font_ = name_font_.DeriveFont(0, gfx::Font::ITALIC);
121 #endif
122 #endif
123 }
124 
~AutofillPopupControllerImpl()125 AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {}
126 
Show(const std::vector<base::string16> & names,const std::vector<base::string16> & subtexts,const std::vector<base::string16> & icons,const std::vector<int> & identifiers)127 void AutofillPopupControllerImpl::Show(
128     const std::vector<base::string16>& names,
129     const std::vector<base::string16>& subtexts,
130     const std::vector<base::string16>& icons,
131     const std::vector<int>& identifiers) {
132   SetValues(names, subtexts, icons, identifiers);
133 
134 #if !defined(OS_ANDROID)
135   // Android displays the long text with ellipsis using the view attributes.
136 
137   UpdatePopupBounds();
138   int popup_width = popup_bounds().width();
139 
140   // Elide the name and subtext strings so that the popup fits in the available
141   // space.
142   for (size_t i = 0; i < names_.size(); ++i) {
143     int name_width = GetNameFontForRow(i).GetStringWidth(names_[i]);
144     int subtext_width = subtext_font().GetStringWidth(subtexts_[i]);
145     int total_text_length = name_width + subtext_width;
146 
147     // The line can have no strings if it represents a UI element, such as
148     // a separator line.
149     if (total_text_length == 0)
150       continue;
151 
152     int available_width = popup_width - RowWidthWithoutText(i);
153 
154     // Each field recieves space in proportion to its length.
155     int name_size = available_width * name_width / total_text_length;
156     names_[i] = gfx::ElideText(names_[i],
157                               GetNameFontForRow(i),
158                               name_size,
159                               gfx::ELIDE_AT_END);
160 
161     int subtext_size = available_width * subtext_width / total_text_length;
162     subtexts_[i] = gfx::ElideText(subtexts_[i],
163                                  subtext_font(),
164                                  subtext_size,
165                                  gfx::ELIDE_AT_END);
166   }
167 #endif
168 
169   if (!view_) {
170     view_ = AutofillPopupView::Create(this);
171 
172     // It is possible to fail to create the popup, in this case
173     // treat the popup as hiding right away.
174     if (!view_) {
175       Hide();
176       return;
177     }
178 
179     ShowView();
180   } else {
181     UpdateBoundsAndRedrawPopup();
182   }
183 
184   delegate_->OnPopupShown();
185   if (web_contents_ && !registered_key_press_event_callback_with_) {
186     registered_key_press_event_callback_with_ =
187         web_contents_->GetRenderViewHost();
188     registered_key_press_event_callback_with_->AddKeyPressEventCallback(
189         key_press_event_callback_);
190   }
191 }
192 
UpdateDataListValues(const std::vector<base::string16> & values,const std::vector<base::string16> & labels)193 void AutofillPopupControllerImpl::UpdateDataListValues(
194     const std::vector<base::string16>& values,
195     const std::vector<base::string16>& labels) {
196   // Remove all the old data list values, which should always be at the top of
197   // the list if they are present.
198   while (!identifiers_.empty() &&
199          identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry) {
200     names_.erase(names_.begin());
201     subtexts_.erase(subtexts_.begin());
202     icons_.erase(icons_.begin());
203     identifiers_.erase(identifiers_.begin());
204   }
205 
206   // If there are no new data list values, exit (clearing the separator if there
207   // is one).
208   if (values.empty()) {
209     if (!identifiers_.empty() &&
210         identifiers_[0] == WebAutofillClient::MenuItemIDSeparator) {
211       names_.erase(names_.begin());
212       subtexts_.erase(subtexts_.begin());
213       icons_.erase(icons_.begin());
214       identifiers_.erase(identifiers_.begin());
215     }
216 
217      // The popup contents have changed, so either update the bounds or hide it.
218     if (HasSuggestions())
219       UpdateBoundsAndRedrawPopup();
220     else
221       Hide();
222 
223     return;
224   }
225 
226   // Add a separator if there are any other values.
227   if (!identifiers_.empty() &&
228       identifiers_[0] != WebAutofillClient::MenuItemIDSeparator) {
229     names_.insert(names_.begin(), base::string16());
230     subtexts_.insert(subtexts_.begin(), base::string16());
231     icons_.insert(icons_.begin(), base::string16());
232     identifiers_.insert(identifiers_.begin(),
233                         WebAutofillClient::MenuItemIDSeparator);
234   }
235 
236 
237   names_.insert(names_.begin(), values.begin(), values.end());
238   subtexts_.insert(subtexts_.begin(), labels.begin(), labels.end());
239 
240   // Add the values that are the same for all data list elements.
241   icons_.insert(icons_.begin(), values.size(), base::string16());
242   identifiers_.insert(identifiers_.begin(),
243                       values.size(),
244                       WebAutofillClient::MenuItemIDDataListEntry);
245 
246   UpdateBoundsAndRedrawPopup();
247 }
248 
Hide()249 void AutofillPopupControllerImpl::Hide() {
250   if (web_contents_ && (!web_contents_->IsBeingDestroyed()) &&
251       (registered_key_press_event_callback_with_ ==
252           web_contents_->GetRenderViewHost())) {
253     web_contents_->GetRenderViewHost()->RemoveKeyPressEventCallback(
254         key_press_event_callback_);
255   }
256   registered_key_press_event_callback_with_ = NULL;
257 
258   if (delegate_.get())
259     delegate_->OnPopupHidden();
260 
261   if (view_)
262     view_->Hide();
263 
264   delete this;
265 }
266 
ViewDestroyed()267 void AutofillPopupControllerImpl::ViewDestroyed() {
268   // The view has already been destroyed so clear the reference to it.
269   view_ = NULL;
270 
271   Hide();
272 }
273 
HandleKeyPressEvent(const content::NativeWebKeyboardEvent & event)274 bool AutofillPopupControllerImpl::HandleKeyPressEvent(
275     const content::NativeWebKeyboardEvent& event) {
276   switch (event.windowsKeyCode) {
277     case ui::VKEY_UP:
278       SelectPreviousLine();
279       return true;
280     case ui::VKEY_DOWN:
281       SelectNextLine();
282       return true;
283     case ui::VKEY_PRIOR:  // Page up.
284       SetSelectedLine(0);
285       return true;
286     case ui::VKEY_NEXT:  // Page down.
287       SetSelectedLine(names().size() - 1);
288       return true;
289     case ui::VKEY_ESCAPE:
290       Hide();
291       return true;
292     case ui::VKEY_DELETE:
293       return (event.modifiers & content::NativeWebKeyboardEvent::ShiftKey) &&
294              RemoveSelectedLine();
295     case ui::VKEY_TAB:
296       // A tab press should cause the selected line to be accepted, but still
297       // return false so the tab key press propagates and changes the cursor
298       // location.
299       AcceptSelectedLine();
300       return false;
301     case ui::VKEY_RETURN:
302       return AcceptSelectedLine();
303     default:
304       return false;
305   }
306 }
307 
UpdateBoundsAndRedrawPopup()308 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() {
309 #if !defined(OS_ANDROID)
310   // TODO(csharp): Since UpdatePopupBounds can change the position of the popup,
311   // the popup could end up jumping from above the element to below it.
312   // It is unclear if it is better to keep the popup where it was, or if it
313   // should try and move to its desired position.
314   UpdatePopupBounds();
315 #endif
316 
317   view_->UpdateBoundsAndRedrawPopup();
318 }
319 
LineSelectedAtPoint(int x,int y)320 void AutofillPopupControllerImpl::LineSelectedAtPoint(int x, int y) {
321   SetSelectedLine(LineFromY(y));
322 }
323 
LineAcceptedAtPoint(int x,int y)324 void AutofillPopupControllerImpl::LineAcceptedAtPoint(int x, int y) {
325   LineSelectedAtPoint(x, y);
326   AcceptSelectedLine();
327 }
328 
SelectionCleared()329 void AutofillPopupControllerImpl::SelectionCleared() {
330   SetSelectedLine(kNoSelection);
331 }
332 
ShouldRepostEvent(const ui::MouseEvent & event)333 bool AutofillPopupControllerImpl::ShouldRepostEvent(
334     const ui::MouseEvent& event) {
335   return delegate_->ShouldRepostEvent(event);
336 }
337 
AcceptSuggestion(size_t index)338 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) {
339   delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]);
340 }
341 
GetIconResourceID(const base::string16 & resource_name) const342 int AutofillPopupControllerImpl::GetIconResourceID(
343     const base::string16& resource_name) const {
344   for (size_t i = 0; i < arraysize(kDataResources); ++i) {
345     if (resource_name == ASCIIToUTF16(kDataResources[i].name))
346       return kDataResources[i].id;
347   }
348 
349   return -1;
350 }
351 
CanDelete(size_t index) const352 bool AutofillPopupControllerImpl::CanDelete(size_t index) const {
353   // TODO(isherman): Native AddressBook suggestions on Mac and Android should
354   // not be considered to be deleteable.
355   int id = identifiers_[index];
356   return id > 0 ||
357       id == WebAutofillClient::MenuItemIDAutocompleteEntry ||
358       id == WebAutofillClient::MenuItemIDPasswordEntry;
359 }
360 
IsWarning(size_t index) const361 bool AutofillPopupControllerImpl::IsWarning(size_t index) const {
362   return identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage;
363 }
364 
GetRowBounds(size_t index)365 gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) {
366   int top = AutofillPopupView::kBorderThickness;
367   for (size_t i = 0; i < index; ++i) {
368     top += GetRowHeightFromId(identifiers()[i]);
369   }
370 
371   return gfx::Rect(
372       AutofillPopupView::kBorderThickness,
373       top,
374       popup_bounds_.width() - 2 * AutofillPopupView::kBorderThickness,
375       GetRowHeightFromId(identifiers()[index]));
376 }
377 
SetPopupBounds(const gfx::Rect & bounds)378 void AutofillPopupControllerImpl::SetPopupBounds(const gfx::Rect& bounds) {
379   popup_bounds_ = bounds;
380   UpdateBoundsAndRedrawPopup();
381 }
382 
popup_bounds() const383 const gfx::Rect& AutofillPopupControllerImpl::popup_bounds() const {
384   return popup_bounds_;
385 }
386 
container_view() const387 gfx::NativeView AutofillPopupControllerImpl::container_view() const {
388   return container_view_;
389 }
390 
element_bounds() const391 const gfx::RectF& AutofillPopupControllerImpl::element_bounds() const {
392   return element_bounds_;
393 }
394 
IsRTL() const395 bool AutofillPopupControllerImpl::IsRTL() const {
396   return text_direction_ == base::i18n::RIGHT_TO_LEFT;
397 }
398 
hide_on_outside_click() const399 bool AutofillPopupControllerImpl::hide_on_outside_click() const {
400   return hide_on_outside_click_;
401 }
402 
names() const403 const std::vector<base::string16>& AutofillPopupControllerImpl::names() const {
404   return names_;
405 }
406 
subtexts() const407 const std::vector<base::string16>& AutofillPopupControllerImpl::subtexts()
408     const {
409   return subtexts_;
410 }
411 
icons() const412 const std::vector<base::string16>& AutofillPopupControllerImpl::icons() const {
413   return icons_;
414 }
415 
identifiers() const416 const std::vector<int>& AutofillPopupControllerImpl::identifiers() const {
417   return identifiers_;
418 }
419 
420 #if !defined(OS_ANDROID)
GetNameFontForRow(size_t index) const421 const gfx::Font& AutofillPopupControllerImpl::GetNameFontForRow(size_t index)
422     const {
423   if (identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage)
424     return warning_font_;
425 
426   return name_font_;
427 }
428 
subtext_font() const429 const gfx::Font& AutofillPopupControllerImpl::subtext_font() const {
430   return subtext_font_;
431 }
432 #endif
433 
selected_line() const434 int AutofillPopupControllerImpl::selected_line() const {
435   return selected_line_;
436 }
437 
set_hide_on_outside_click(bool hide_on_outside_click)438 void AutofillPopupControllerImpl::set_hide_on_outside_click(
439     bool hide_on_outside_click) {
440   hide_on_outside_click_ = hide_on_outside_click;
441 }
442 
SetSelectedLine(int selected_line)443 void AutofillPopupControllerImpl::SetSelectedLine(int selected_line) {
444   if (selected_line_ == selected_line)
445     return;
446 
447   if (selected_line_ != kNoSelection &&
448       static_cast<size_t>(selected_line_) < identifiers_.size())
449     InvalidateRow(selected_line_);
450 
451   if (selected_line != kNoSelection)
452     InvalidateRow(selected_line);
453 
454   selected_line_ = selected_line;
455 
456   if (selected_line_ != kNoSelection)
457     delegate_->DidSelectSuggestion(identifiers_[selected_line_]);
458   else
459     delegate_->ClearPreviewedForm();
460 }
461 
SelectNextLine()462 void AutofillPopupControllerImpl::SelectNextLine() {
463   int new_selected_line = selected_line_ + 1;
464 
465   // Skip over any lines that can't be selected.
466   while (static_cast<size_t>(new_selected_line) < names_.size() &&
467          !CanAccept(identifiers()[new_selected_line])) {
468     ++new_selected_line;
469   }
470 
471   if (new_selected_line >= static_cast<int>(names_.size()))
472     new_selected_line = 0;
473 
474   SetSelectedLine(new_selected_line);
475 }
476 
SelectPreviousLine()477 void AutofillPopupControllerImpl::SelectPreviousLine() {
478   int new_selected_line = selected_line_ - 1;
479 
480   // Skip over any lines that can't be selected.
481   while (new_selected_line > kNoSelection &&
482          !CanAccept(identifiers()[new_selected_line])) {
483     --new_selected_line;
484   }
485 
486   if (new_selected_line <= kNoSelection)
487     new_selected_line = names_.size() - 1;
488 
489   SetSelectedLine(new_selected_line);
490 }
491 
AcceptSelectedLine()492 bool AutofillPopupControllerImpl::AcceptSelectedLine() {
493   if (selected_line_ == kNoSelection)
494     return false;
495 
496   DCHECK_GE(selected_line_, 0);
497   DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
498 
499   if (!CanAccept(identifiers_[selected_line_]))
500     return false;
501 
502   AcceptSuggestion(selected_line_);
503   return true;
504 }
505 
RemoveSelectedLine()506 bool AutofillPopupControllerImpl::RemoveSelectedLine() {
507   if (selected_line_ == kNoSelection)
508     return false;
509 
510   DCHECK_GE(selected_line_, 0);
511   DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
512 
513   if (!CanDelete(selected_line_))
514     return false;
515 
516   delegate_->RemoveSuggestion(full_names_[selected_line_],
517                               identifiers_[selected_line_]);
518 
519   // Remove the deleted element.
520   names_.erase(names_.begin() + selected_line_);
521   full_names_.erase(full_names_.begin() + selected_line_);
522   subtexts_.erase(subtexts_.begin() + selected_line_);
523   icons_.erase(icons_.begin() + selected_line_);
524   identifiers_.erase(identifiers_.begin() + selected_line_);
525 
526   SetSelectedLine(kNoSelection);
527 
528   if (HasSuggestions()) {
529     delegate_->ClearPreviewedForm();
530     UpdateBoundsAndRedrawPopup();
531   } else {
532     Hide();
533   }
534 
535   return true;
536 }
537 
LineFromY(int y)538 int AutofillPopupControllerImpl::LineFromY(int y) {
539   int current_height = AutofillPopupView::kBorderThickness;
540 
541   for (size_t i = 0; i < identifiers().size(); ++i) {
542     current_height += GetRowHeightFromId(identifiers()[i]);
543 
544     if (y <= current_height)
545       return i;
546   }
547 
548   // The y value goes beyond the popup so stop the selection at the last line.
549   return identifiers().size() - 1;
550 }
551 
GetRowHeightFromId(int identifier) const552 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const {
553   if (identifier == WebAutofillClient::MenuItemIDSeparator)
554     return kSeparatorHeight;
555 
556   return kRowHeight;
557 }
558 
CanAccept(int id)559 bool AutofillPopupControllerImpl::CanAccept(int id) {
560   return id != WebAutofillClient::MenuItemIDSeparator &&
561       id != WebAutofillClient::MenuItemIDWarningMessage;
562 }
563 
HasSuggestions()564 bool AutofillPopupControllerImpl::HasSuggestions() {
565   return identifiers_.size() != 0 &&
566       (identifiers_[0] > 0 ||
567        identifiers_[0] ==
568            WebAutofillClient::MenuItemIDAutocompleteEntry ||
569        identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry ||
570        identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry);
571 }
572 
SetValues(const std::vector<base::string16> & names,const std::vector<base::string16> & subtexts,const std::vector<base::string16> & icons,const std::vector<int> & identifiers)573 void AutofillPopupControllerImpl::SetValues(
574     const std::vector<base::string16>& names,
575     const std::vector<base::string16>& subtexts,
576     const std::vector<base::string16>& icons,
577     const std::vector<int>& identifiers) {
578   names_ = names;
579   full_names_ = names;
580   subtexts_ = subtexts;
581   icons_ = icons;
582   identifiers_ = identifiers;
583 }
584 
ShowView()585 void AutofillPopupControllerImpl::ShowView() {
586   view_->Show();
587 }
588 
InvalidateRow(size_t row)589 void AutofillPopupControllerImpl::InvalidateRow(size_t row) {
590   DCHECK(0 <= row);
591   DCHECK(row < identifiers_.size());
592   view_->InvalidateRow(row);
593 }
594 
595 #if !defined(OS_ANDROID)
GetDesiredPopupWidth() const596 int AutofillPopupControllerImpl::GetDesiredPopupWidth() const {
597   if (!name_font_.platform_font() || !subtext_font_.platform_font()) {
598     // We can't calculate the size of the popup if the fonts
599     // aren't present.
600     return 0;
601   }
602 
603   int popup_width = RoundedElementBounds().width();
604   DCHECK_EQ(names().size(), subtexts().size());
605   for (size_t i = 0; i < names().size(); ++i) {
606     int row_size = name_font_.GetStringWidth(names()[i]) +
607         subtext_font_.GetStringWidth(subtexts()[i]) +
608         RowWidthWithoutText(i);
609 
610     popup_width = std::max(popup_width, row_size);
611   }
612 
613   return popup_width;
614 }
615 
GetDesiredPopupHeight() const616 int AutofillPopupControllerImpl::GetDesiredPopupHeight() const {
617   int popup_height = 2 * AutofillPopupView::kBorderThickness;
618 
619   for (size_t i = 0; i < identifiers().size(); ++i) {
620     popup_height += GetRowHeightFromId(identifiers()[i]);
621   }
622 
623   return popup_height;
624 }
625 
RowWidthWithoutText(int row) const626 int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const {
627   int row_size = kEndPadding;
628 
629   if (!subtexts_[row].empty())
630     row_size += kNamePadding;
631 
632   // Add the Autofill icon size, if required.
633   if (!icons_[row].empty()) {
634     int icon_width = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
635         GetIconResourceID(icons_[row])).Width();
636     row_size += icon_width + kIconPadding;
637   }
638 
639   // Add the padding at the end.
640   row_size += kEndPadding;
641 
642   // Add room for the popup border.
643   row_size += 2 * AutofillPopupView::kBorderThickness;
644 
645   return row_size;
646 }
647 
UpdatePopupBounds()648 void AutofillPopupControllerImpl::UpdatePopupBounds() {
649   int popup_required_width = GetDesiredPopupWidth();
650   int popup_height = GetDesiredPopupHeight();
651   // This is the top left point of the popup if the popup is above the element
652   // and grows to the left (since that is the highest and furthest left the
653   // popup go could).
654   gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() +
655       gfx::Vector2d(RoundedElementBounds().width() - popup_required_width,
656                     -popup_height);
657 
658   // This is the bottom right point of the popup if the popup is below the
659   // element and grows to the right (since the is the lowest and furthest right
660   // the popup could go).
661   gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() +
662       gfx::Vector2d(popup_required_width,
663                     RoundedElementBounds().height() + popup_height);
664 
665   gfx::Display top_left_display = GetDisplayNearestPoint(
666       top_left_corner_of_popup);
667   gfx::Display bottom_right_display = GetDisplayNearestPoint(
668       bottom_right_corner_of_popup);
669 
670   std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth(
671       top_left_display, bottom_right_display, popup_required_width);
672   std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight(
673       top_left_display, bottom_right_display, popup_height);
674 
675   popup_bounds_ = gfx::Rect(popup_x_and_width.first,
676                             popup_y_and_height.first,
677                             popup_x_and_width.second,
678                             popup_y_and_height.second);
679 }
680 #endif  // !defined(OS_ANDROID)
681 
GetWeakPtr()682 WeakPtr<AutofillPopupControllerImpl> AutofillPopupControllerImpl::GetWeakPtr() {
683   return weak_ptr_factory_.GetWeakPtr();
684 }
685 
ClearState()686 void AutofillPopupControllerImpl::ClearState() {
687   // Don't clear view_, because otherwise the popup will have to get regenerated
688   // and this will cause flickering.
689 
690   popup_bounds_ = gfx::Rect();
691 
692   names_.clear();
693   subtexts_.clear();
694   icons_.clear();
695   identifiers_.clear();
696   full_names_.clear();
697 
698   selected_line_ = kNoSelection;
699 }
700 
RoundedElementBounds() const701 const gfx::Rect AutofillPopupControllerImpl::RoundedElementBounds() const {
702   return gfx::ToEnclosingRect(element_bounds_);
703 }
704 
GetDisplayNearestPoint(const gfx::Point & point) const705 gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint(
706     const gfx::Point& point) const {
707   return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint(
708       point);
709 }
710 
CalculatePopupXAndWidth(const gfx::Display & left_display,const gfx::Display & right_display,int popup_required_width) const711 std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth(
712     const gfx::Display& left_display,
713     const gfx::Display& right_display,
714     int popup_required_width) const {
715   int leftmost_display_x = left_display.bounds().x();
716   int rightmost_display_x =
717       right_display.GetSizeInPixel().width() + right_display.bounds().x();
718 
719   // Calculate the start coordinates for the popup if it is growing right or
720   // the end position if it is growing to the left, capped to screen space.
721   int right_growth_start = std::max(leftmost_display_x,
722                                     std::min(rightmost_display_x,
723                                              RoundedElementBounds().x()));
724   int left_growth_end = std::max(leftmost_display_x,
725                                    std::min(rightmost_display_x,
726                                             RoundedElementBounds().right()));
727 
728   int right_available = rightmost_display_x - right_growth_start;
729   int left_available = left_growth_end - leftmost_display_x;
730 
731   int popup_width = std::min(popup_required_width,
732                              std::max(right_available, left_available));
733 
734   // If there is enough space for the popup on the right, show it there,
735   // otherwise choose the larger size.
736   if (right_available >= popup_width || right_available >= left_available)
737     return std::make_pair(right_growth_start, popup_width);
738   else
739     return std::make_pair(left_growth_end - popup_width, popup_width);
740 }
741 
CalculatePopupYAndHeight(const gfx::Display & top_display,const gfx::Display & bottom_display,int popup_required_height) const742 std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight(
743     const gfx::Display& top_display,
744     const gfx::Display& bottom_display,
745     int popup_required_height) const {
746   int topmost_display_y = top_display.bounds().y();
747   int bottommost_display_y =
748       bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y();
749 
750   // Calculate the start coordinates for the popup if it is growing down or
751   // the end position if it is growing up, capped to screen space.
752   int top_growth_end = std::max(topmost_display_y,
753                                 std::min(bottommost_display_y,
754                                          RoundedElementBounds().y()));
755   int bottom_growth_start = std::max(topmost_display_y,
756       std::min(bottommost_display_y, RoundedElementBounds().bottom()));
757 
758   int top_available = bottom_growth_start - topmost_display_y;
759   int bottom_available = bottommost_display_y - top_growth_end;
760 
761   // TODO(csharp): Restrict the popup height to what is available.
762   if (bottom_available >= popup_required_height ||
763       bottom_available >= top_available) {
764     // The popup can appear below the field.
765     return std::make_pair(bottom_growth_start, popup_required_height);
766   } else {
767     // The popup must appear above the field.
768     return std::make_pair(top_growth_end - popup_required_height,
769                           popup_required_height);
770   }
771 }
772 
773 }  // namespace autofill
774