autofill_popup_controller_impl.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
27using base::WeakPtr;
28using blink::WebAutofillClient;
29
30namespace autofill {
31namespace {
32
33// Used to indicate that no line is currently selected by the user.
34const int kNoSelection = -1;
35
36// The vertical height of each row in pixels.
37const size_t kRowHeight = 24;
38
39// The vertical height of a separator in pixels.
40const size_t kSeparatorHeight = 1;
41
42#if !defined(OS_ANDROID)
43// Size difference between name and subtext in pixels.
44const int kLabelFontSizeDelta = -2;
45
46const size_t kNamePadding = AutofillPopupView::kNamePadding;
47const size_t kIconPadding = AutofillPopupView::kIconPadding;
48const size_t kEndPadding = AutofillPopupView::kEndPadding;
49#endif
50
51struct DataResource {
52  const char* name;
53  int id;
54};
55
56const 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
69WeakPtr<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
95AutofillPopupControllerImpl::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
125AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {}
126
127void AutofillPopupControllerImpl::Show(
128    const std::vector<string16>& names,
129    const std::vector<string16>& subtexts,
130    const std::vector<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
193void 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(), string16());
230    subtexts_.insert(subtexts_.begin(), string16());
231    icons_.insert(icons_.begin(), 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
249void 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
267void AutofillPopupControllerImpl::ViewDestroyed() {
268  // The view has already been destroyed so clear the reference to it.
269  view_ = NULL;
270
271  Hide();
272}
273
274bool 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
308void 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
320void AutofillPopupControllerImpl::LineSelectedAtPoint(int x, int y) {
321  SetSelectedLine(LineFromY(y));
322}
323
324void AutofillPopupControllerImpl::LineAcceptedAtPoint(int x, int y) {
325  LineSelectedAtPoint(x, y);
326  AcceptSelectedLine();
327}
328
329void AutofillPopupControllerImpl::SelectionCleared() {
330  SetSelectedLine(kNoSelection);
331}
332
333bool AutofillPopupControllerImpl::ShouldRepostEvent(
334    const ui::MouseEvent& event) {
335  return delegate_->ShouldRepostEvent(event);
336}
337
338void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) {
339  delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]);
340}
341
342int AutofillPopupControllerImpl::GetIconResourceID(
343    const 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
352bool 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
361bool AutofillPopupControllerImpl::IsWarning(size_t index) const {
362  return identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage;
363}
364
365gfx::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
378void AutofillPopupControllerImpl::SetPopupBounds(const gfx::Rect& bounds) {
379  popup_bounds_ = bounds;
380  UpdateBoundsAndRedrawPopup();
381}
382
383const gfx::Rect& AutofillPopupControllerImpl::popup_bounds() const {
384  return popup_bounds_;
385}
386
387gfx::NativeView AutofillPopupControllerImpl::container_view() const {
388  return container_view_;
389}
390
391const gfx::RectF& AutofillPopupControllerImpl::element_bounds() const {
392  return element_bounds_;
393}
394
395bool AutofillPopupControllerImpl::IsRTL() const {
396  return text_direction_ == base::i18n::RIGHT_TO_LEFT;
397}
398
399bool AutofillPopupControllerImpl::hide_on_outside_click() const {
400  return hide_on_outside_click_;
401}
402
403const std::vector<string16>& AutofillPopupControllerImpl::names() const {
404  return names_;
405}
406
407const std::vector<string16>& AutofillPopupControllerImpl::subtexts() const {
408  return subtexts_;
409}
410
411const std::vector<string16>& AutofillPopupControllerImpl::icons() const {
412  return icons_;
413}
414
415const std::vector<int>& AutofillPopupControllerImpl::identifiers() const {
416  return identifiers_;
417}
418
419#if !defined(OS_ANDROID)
420const gfx::Font& AutofillPopupControllerImpl::GetNameFontForRow(size_t index)
421    const {
422  if (identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage)
423    return warning_font_;
424
425  return name_font_;
426}
427
428const gfx::Font& AutofillPopupControllerImpl::subtext_font() const {
429  return subtext_font_;
430}
431#endif
432
433int AutofillPopupControllerImpl::selected_line() const {
434  return selected_line_;
435}
436
437void AutofillPopupControllerImpl::set_hide_on_outside_click(
438    bool hide_on_outside_click) {
439  hide_on_outside_click_ = hide_on_outside_click;
440}
441
442void AutofillPopupControllerImpl::SetSelectedLine(int selected_line) {
443  if (selected_line_ == selected_line)
444    return;
445
446  if (selected_line_ != kNoSelection &&
447      static_cast<size_t>(selected_line_) < identifiers_.size())
448    InvalidateRow(selected_line_);
449
450  if (selected_line != kNoSelection)
451    InvalidateRow(selected_line);
452
453  selected_line_ = selected_line;
454
455  if (selected_line_ != kNoSelection)
456    delegate_->DidSelectSuggestion(identifiers_[selected_line_]);
457  else
458    delegate_->ClearPreviewedForm();
459}
460
461void AutofillPopupControllerImpl::SelectNextLine() {
462  int new_selected_line = selected_line_ + 1;
463
464  // Skip over any lines that can't be selected.
465  while (static_cast<size_t>(new_selected_line) < names_.size() &&
466         !CanAccept(identifiers()[new_selected_line])) {
467    ++new_selected_line;
468  }
469
470  if (new_selected_line >= static_cast<int>(names_.size()))
471    new_selected_line = 0;
472
473  SetSelectedLine(new_selected_line);
474}
475
476void AutofillPopupControllerImpl::SelectPreviousLine() {
477  int new_selected_line = selected_line_ - 1;
478
479  // Skip over any lines that can't be selected.
480  while (new_selected_line > kNoSelection &&
481         !CanAccept(identifiers()[new_selected_line])) {
482    --new_selected_line;
483  }
484
485  if (new_selected_line <= kNoSelection)
486    new_selected_line = names_.size() - 1;
487
488  SetSelectedLine(new_selected_line);
489}
490
491bool AutofillPopupControllerImpl::AcceptSelectedLine() {
492  if (selected_line_ == kNoSelection)
493    return false;
494
495  DCHECK_GE(selected_line_, 0);
496  DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
497
498  if (!CanAccept(identifiers_[selected_line_]))
499    return false;
500
501  AcceptSuggestion(selected_line_);
502  return true;
503}
504
505bool AutofillPopupControllerImpl::RemoveSelectedLine() {
506  if (selected_line_ == kNoSelection)
507    return false;
508
509  DCHECK_GE(selected_line_, 0);
510  DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
511
512  if (!CanDelete(selected_line_))
513    return false;
514
515  delegate_->RemoveSuggestion(full_names_[selected_line_],
516                              identifiers_[selected_line_]);
517
518  // Remove the deleted element.
519  names_.erase(names_.begin() + selected_line_);
520  full_names_.erase(full_names_.begin() + selected_line_);
521  subtexts_.erase(subtexts_.begin() + selected_line_);
522  icons_.erase(icons_.begin() + selected_line_);
523  identifiers_.erase(identifiers_.begin() + selected_line_);
524
525  SetSelectedLine(kNoSelection);
526
527  if (HasSuggestions()) {
528    delegate_->ClearPreviewedForm();
529    UpdateBoundsAndRedrawPopup();
530  } else {
531    Hide();
532  }
533
534  return true;
535}
536
537int AutofillPopupControllerImpl::LineFromY(int y) {
538  int current_height = AutofillPopupView::kBorderThickness;
539
540  for (size_t i = 0; i < identifiers().size(); ++i) {
541    current_height += GetRowHeightFromId(identifiers()[i]);
542
543    if (y <= current_height)
544      return i;
545  }
546
547  // The y value goes beyond the popup so stop the selection at the last line.
548  return identifiers().size() - 1;
549}
550
551int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const {
552  if (identifier == WebAutofillClient::MenuItemIDSeparator)
553    return kSeparatorHeight;
554
555  return kRowHeight;
556}
557
558bool AutofillPopupControllerImpl::CanAccept(int id) {
559  return id != WebAutofillClient::MenuItemIDSeparator &&
560      id != WebAutofillClient::MenuItemIDWarningMessage;
561}
562
563bool AutofillPopupControllerImpl::HasSuggestions() {
564  return identifiers_.size() != 0 &&
565      (identifiers_[0] > 0 ||
566       identifiers_[0] ==
567           WebAutofillClient::MenuItemIDAutocompleteEntry ||
568       identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry ||
569       identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry);
570}
571
572void AutofillPopupControllerImpl::SetValues(
573    const std::vector<string16>& names,
574    const std::vector<string16>& subtexts,
575    const std::vector<string16>& icons,
576    const std::vector<int>& identifiers) {
577  names_ = names;
578  full_names_ = names;
579  subtexts_ = subtexts;
580  icons_ = icons;
581  identifiers_ = identifiers;
582}
583
584void AutofillPopupControllerImpl::ShowView() {
585  view_->Show();
586}
587
588void AutofillPopupControllerImpl::InvalidateRow(size_t row) {
589  DCHECK(0 <= row);
590  DCHECK(row < identifiers_.size());
591  view_->InvalidateRow(row);
592}
593
594#if !defined(OS_ANDROID)
595int AutofillPopupControllerImpl::GetDesiredPopupWidth() const {
596  if (!name_font_.platform_font() || !subtext_font_.platform_font()) {
597    // We can't calculate the size of the popup if the fonts
598    // aren't present.
599    return 0;
600  }
601
602  int popup_width = RoundedElementBounds().width();
603  DCHECK_EQ(names().size(), subtexts().size());
604  for (size_t i = 0; i < names().size(); ++i) {
605    int row_size = name_font_.GetStringWidth(names()[i]) +
606        subtext_font_.GetStringWidth(subtexts()[i]) +
607        RowWidthWithoutText(i);
608
609    popup_width = std::max(popup_width, row_size);
610  }
611
612  return popup_width;
613}
614
615int AutofillPopupControllerImpl::GetDesiredPopupHeight() const {
616  int popup_height = 2 * AutofillPopupView::kBorderThickness;
617
618  for (size_t i = 0; i < identifiers().size(); ++i) {
619    popup_height += GetRowHeightFromId(identifiers()[i]);
620  }
621
622  return popup_height;
623}
624
625int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const {
626  int row_size = kEndPadding;
627
628  if (!subtexts_[row].empty())
629    row_size += kNamePadding;
630
631  // Add the Autofill icon size, if required.
632  if (!icons_[row].empty()) {
633    int icon_width = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
634        GetIconResourceID(icons_[row])).Width();
635    row_size += icon_width + kIconPadding;
636  }
637
638  // Add the padding at the end.
639  row_size += kEndPadding;
640
641  // Add room for the popup border.
642  row_size += 2 * AutofillPopupView::kBorderThickness;
643
644  return row_size;
645}
646
647void AutofillPopupControllerImpl::UpdatePopupBounds() {
648  int popup_required_width = GetDesiredPopupWidth();
649  int popup_height = GetDesiredPopupHeight();
650  // This is the top left point of the popup if the popup is above the element
651  // and grows to the left (since that is the highest and furthest left the
652  // popup go could).
653  gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() +
654      gfx::Vector2d(RoundedElementBounds().width() - popup_required_width,
655                    -popup_height);
656
657  // This is the bottom right point of the popup if the popup is below the
658  // element and grows to the right (since the is the lowest and furthest right
659  // the popup could go).
660  gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() +
661      gfx::Vector2d(popup_required_width,
662                    RoundedElementBounds().height() + popup_height);
663
664  gfx::Display top_left_display = GetDisplayNearestPoint(
665      top_left_corner_of_popup);
666  gfx::Display bottom_right_display = GetDisplayNearestPoint(
667      bottom_right_corner_of_popup);
668
669  std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth(
670      top_left_display, bottom_right_display, popup_required_width);
671  std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight(
672      top_left_display, bottom_right_display, popup_height);
673
674  popup_bounds_ = gfx::Rect(popup_x_and_width.first,
675                            popup_y_and_height.first,
676                            popup_x_and_width.second,
677                            popup_y_and_height.second);
678}
679#endif  // !defined(OS_ANDROID)
680
681WeakPtr<AutofillPopupControllerImpl> AutofillPopupControllerImpl::GetWeakPtr() {
682  return weak_ptr_factory_.GetWeakPtr();
683}
684
685void AutofillPopupControllerImpl::ClearState() {
686  // Don't clear view_, because otherwise the popup will have to get regenerated
687  // and this will cause flickering.
688
689  popup_bounds_ = gfx::Rect();
690
691  names_.clear();
692  subtexts_.clear();
693  icons_.clear();
694  identifiers_.clear();
695  full_names_.clear();
696
697  selected_line_ = kNoSelection;
698}
699
700const gfx::Rect AutofillPopupControllerImpl::RoundedElementBounds() const {
701  return gfx::ToEnclosingRect(element_bounds_);
702}
703
704gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint(
705    const gfx::Point& point) const {
706  return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint(
707      point);
708}
709
710std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth(
711    const gfx::Display& left_display,
712    const gfx::Display& right_display,
713    int popup_required_width) const {
714  int leftmost_display_x = left_display.bounds().x();
715  int rightmost_display_x =
716      right_display.GetSizeInPixel().width() + right_display.bounds().x();
717
718  // Calculate the start coordinates for the popup if it is growing right or
719  // the end position if it is growing to the left, capped to screen space.
720  int right_growth_start = std::max(leftmost_display_x,
721                                    std::min(rightmost_display_x,
722                                             RoundedElementBounds().x()));
723  int left_growth_end = std::max(leftmost_display_x,
724                                   std::min(rightmost_display_x,
725                                            RoundedElementBounds().right()));
726
727  int right_available = rightmost_display_x - right_growth_start;
728  int left_available = left_growth_end - leftmost_display_x;
729
730  int popup_width = std::min(popup_required_width,
731                             std::max(right_available, left_available));
732
733  // If there is enough space for the popup on the right, show it there,
734  // otherwise choose the larger size.
735  if (right_available >= popup_width || right_available >= left_available)
736    return std::make_pair(right_growth_start, popup_width);
737  else
738    return std::make_pair(left_growth_end - popup_width, popup_width);
739}
740
741std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight(
742    const gfx::Display& top_display,
743    const gfx::Display& bottom_display,
744    int popup_required_height) const {
745  int topmost_display_y = top_display.bounds().y();
746  int bottommost_display_y =
747      bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y();
748
749  // Calculate the start coordinates for the popup if it is growing down or
750  // the end position if it is growing up, capped to screen space.
751  int top_growth_end = std::max(topmost_display_y,
752                                std::min(bottommost_display_y,
753                                         RoundedElementBounds().y()));
754  int bottom_growth_start = std::max(topmost_display_y,
755      std::min(bottommost_display_y, RoundedElementBounds().bottom()));
756
757  int top_available = bottom_growth_start - topmost_display_y;
758  int bottom_available = bottommost_display_y - top_growth_end;
759
760  // TODO(csharp): Restrict the popup height to what is available.
761  if (bottom_available >= popup_required_height ||
762      bottom_available >= top_available) {
763    // The popup can appear below the field.
764    return std::make_pair(bottom_growth_start, popup_required_height);
765  } else {
766    // The popup must appear above the field.
767    return std::make_pair(top_growth_end - popup_required_height,
768                          popup_required_height);
769  }
770}
771
772}  // namespace autofill
773