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