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.
5 #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
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/text_utils.h"
26 #include "ui/gfx/vector2d.h"
29 using blink::WebAutofillClient
;
34 // Used to indicate that no line is currently selected by the user.
35 const int kNoSelection
= -1;
37 // The vertical height of each row in pixels.
38 const size_t kRowHeight
= 24;
40 // The vertical height of a separator in pixels.
41 const size_t kSeparatorHeight
= 1;
43 #if !defined(OS_ANDROID)
44 // Size difference between name and subtext in pixels.
45 const int kLabelFontSizeDelta
= -2;
47 const size_t kNamePadding
= AutofillPopupView::kNamePadding
;
48 const size_t kIconPadding
= AutofillPopupView::kIconPadding
;
49 const size_t kEndPadding
= AutofillPopupView::kEndPadding
;
57 const DataResource kDataResources
[] = {
58 { "americanExpressCC", IDR_AUTOFILL_CC_AMEX
},
59 { "dinersCC", IDR_AUTOFILL_CC_DINERS
},
60 { "discoverCC", IDR_AUTOFILL_CC_DISCOVER
},
61 { "genericCC", IDR_AUTOFILL_CC_GENERIC
},
62 { "jcbCC", IDR_AUTOFILL_CC_JCB
},
63 { "masterCardCC", IDR_AUTOFILL_CC_MASTERCARD
},
64 { "visaCC", IDR_AUTOFILL_CC_VISA
},
70 WeakPtr
<AutofillPopupControllerImpl
> AutofillPopupControllerImpl::GetOrCreate(
71 WeakPtr
<AutofillPopupControllerImpl
> previous
,
72 WeakPtr
<AutofillPopupDelegate
> delegate
,
73 content::WebContents
* web_contents
,
74 gfx::NativeView container_view
,
75 const gfx::RectF
& element_bounds
,
76 base::i18n::TextDirection text_direction
) {
77 DCHECK(!previous
.get() || previous
->delegate_
.get() == delegate
.get());
79 if (previous
.get() && previous
->web_contents_
== web_contents
&&
80 previous
->container_view() == container_view
&&
81 previous
->element_bounds() == element_bounds
) {
82 previous
->ClearState();
89 AutofillPopupControllerImpl
* controller
=
90 new AutofillPopupControllerImpl(
91 delegate
, web_contents
, container_view
, element_bounds
,
93 return controller
->GetWeakPtr();
96 AutofillPopupControllerImpl::AutofillPopupControllerImpl(
97 base::WeakPtr
<AutofillPopupDelegate
> delegate
,
98 content::WebContents
* web_contents
,
99 gfx::NativeView container_view
,
100 const gfx::RectF
& element_bounds
,
101 base::i18n::TextDirection text_direction
)
104 web_contents_(web_contents
),
105 container_view_(container_view
),
106 element_bounds_(element_bounds
),
107 text_direction_(text_direction
),
108 registered_key_press_event_callback_with_(NULL
),
109 hide_on_outside_click_(false),
110 key_press_event_callback_(
111 base::Bind(&AutofillPopupControllerImpl::HandleKeyPressEvent
,
112 base::Unretained(this))),
113 weak_ptr_factory_(this) {
115 #if !defined(OS_ANDROID)
116 subtext_font_list_
= name_font_list_
.DeriveFontListWithSizeDelta(
117 kLabelFontSizeDelta
);
118 #if defined(OS_MACOSX)
119 // There is no italic version of the system font.
120 warning_font_list_
= name_font_list_
;
122 warning_font_list_
= name_font_list_
.DeriveFontListWithSizeDeltaAndStyle(
123 0, gfx::Font::ITALIC
);
128 AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {}
130 void AutofillPopupControllerImpl::Show(
131 const std::vector
<base::string16
>& names
,
132 const std::vector
<base::string16
>& subtexts
,
133 const std::vector
<base::string16
>& icons
,
134 const std::vector
<int>& identifiers
) {
135 SetValues(names
, subtexts
, icons
, identifiers
);
137 #if !defined(OS_ANDROID)
138 // Android displays the long text with ellipsis using the view attributes.
141 int popup_width
= popup_bounds().width();
143 // Elide the name and subtext strings so that the popup fits in the available
145 for (size_t i
= 0; i
< names_
.size(); ++i
) {
146 int name_width
= gfx::GetStringWidth(names_
[i
], GetNameFontListForRow(i
));
147 int subtext_width
= gfx::GetStringWidth(subtexts_
[i
], subtext_font_list());
148 int total_text_length
= name_width
+ subtext_width
;
150 // The line can have no strings if it represents a UI element, such as
152 if (total_text_length
== 0)
155 int available_width
= popup_width
- RowWidthWithoutText(i
);
157 // Each field recieves space in proportion to its length.
158 int name_size
= available_width
* name_width
/ total_text_length
;
159 names_
[i
] = gfx::ElideText(names_
[i
],
160 GetNameFontListForRow(i
),
164 int subtext_size
= available_width
* subtext_width
/ total_text_length
;
165 subtexts_
[i
] = gfx::ElideText(subtexts_
[i
],
173 view_
= AutofillPopupView::Create(this);
175 // It is possible to fail to create the popup, in this case
176 // treat the popup as hiding right away.
184 UpdateBoundsAndRedrawPopup();
187 delegate_
->OnPopupShown();
188 if (web_contents_
&& !registered_key_press_event_callback_with_
) {
189 registered_key_press_event_callback_with_
=
190 web_contents_
->GetRenderViewHost();
191 registered_key_press_event_callback_with_
->AddKeyPressEventCallback(
192 key_press_event_callback_
);
196 void AutofillPopupControllerImpl::UpdateDataListValues(
197 const std::vector
<base::string16
>& values
,
198 const std::vector
<base::string16
>& labels
) {
199 // Remove all the old data list values, which should always be at the top of
200 // the list if they are present.
201 while (!identifiers_
.empty() &&
202 identifiers_
[0] == WebAutofillClient::MenuItemIDDataListEntry
) {
203 names_
.erase(names_
.begin());
204 subtexts_
.erase(subtexts_
.begin());
205 icons_
.erase(icons_
.begin());
206 identifiers_
.erase(identifiers_
.begin());
209 // If there are no new data list values, exit (clearing the separator if there
211 if (values
.empty()) {
212 if (!identifiers_
.empty() &&
213 identifiers_
[0] == WebAutofillClient::MenuItemIDSeparator
) {
214 names_
.erase(names_
.begin());
215 subtexts_
.erase(subtexts_
.begin());
216 icons_
.erase(icons_
.begin());
217 identifiers_
.erase(identifiers_
.begin());
220 // The popup contents have changed, so either update the bounds or hide it.
221 if (HasSuggestions())
222 UpdateBoundsAndRedrawPopup();
229 // Add a separator if there are any other values.
230 if (!identifiers_
.empty() &&
231 identifiers_
[0] != WebAutofillClient::MenuItemIDSeparator
) {
232 names_
.insert(names_
.begin(), base::string16());
233 subtexts_
.insert(subtexts_
.begin(), base::string16());
234 icons_
.insert(icons_
.begin(), base::string16());
235 identifiers_
.insert(identifiers_
.begin(),
236 WebAutofillClient::MenuItemIDSeparator
);
240 names_
.insert(names_
.begin(), values
.begin(), values
.end());
241 subtexts_
.insert(subtexts_
.begin(), labels
.begin(), labels
.end());
243 // Add the values that are the same for all data list elements.
244 icons_
.insert(icons_
.begin(), values
.size(), base::string16());
245 identifiers_
.insert(identifiers_
.begin(),
247 WebAutofillClient::MenuItemIDDataListEntry
);
249 UpdateBoundsAndRedrawPopup();
252 void AutofillPopupControllerImpl::Hide() {
253 if (web_contents_
&& (!web_contents_
->IsBeingDestroyed()) &&
254 (registered_key_press_event_callback_with_
==
255 web_contents_
->GetRenderViewHost())) {
256 web_contents_
->GetRenderViewHost()->RemoveKeyPressEventCallback(
257 key_press_event_callback_
);
259 registered_key_press_event_callback_with_
= NULL
;
262 delegate_
->OnPopupHidden();
270 void AutofillPopupControllerImpl::ViewDestroyed() {
271 // The view has already been destroyed so clear the reference to it.
277 bool AutofillPopupControllerImpl::HandleKeyPressEvent(
278 const content::NativeWebKeyboardEvent
& event
) {
279 switch (event
.windowsKeyCode
) {
281 SelectPreviousLine();
286 case ui::VKEY_PRIOR
: // Page up.
289 case ui::VKEY_NEXT
: // Page down.
290 SetSelectedLine(names().size() - 1);
292 case ui::VKEY_ESCAPE
:
295 case ui::VKEY_DELETE
:
296 return (event
.modifiers
& content::NativeWebKeyboardEvent::ShiftKey
) &&
297 RemoveSelectedLine();
299 // A tab press should cause the selected line to be accepted, but still
300 // return false so the tab key press propagates and changes the cursor
302 AcceptSelectedLine();
304 case ui::VKEY_RETURN
:
305 return AcceptSelectedLine();
311 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() {
312 #if !defined(OS_ANDROID)
313 // TODO(csharp): Since UpdatePopupBounds can change the position of the popup,
314 // the popup could end up jumping from above the element to below it.
315 // It is unclear if it is better to keep the popup where it was, or if it
316 // should try and move to its desired position.
320 view_
->UpdateBoundsAndRedrawPopup();
323 void AutofillPopupControllerImpl::LineSelectedAtPoint(int x
, int y
) {
324 SetSelectedLine(LineFromY(y
));
327 void AutofillPopupControllerImpl::LineAcceptedAtPoint(int x
, int y
) {
328 LineSelectedAtPoint(x
, y
);
329 AcceptSelectedLine();
332 void AutofillPopupControllerImpl::SelectionCleared() {
333 SetSelectedLine(kNoSelection
);
336 bool AutofillPopupControllerImpl::ShouldRepostEvent(
337 const ui::MouseEvent
& event
) {
338 return delegate_
->ShouldRepostEvent(event
);
341 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index
) {
342 delegate_
->DidAcceptSuggestion(full_names_
[index
], identifiers_
[index
]);
345 int AutofillPopupControllerImpl::GetIconResourceID(
346 const base::string16
& resource_name
) const {
347 for (size_t i
= 0; i
< arraysize(kDataResources
); ++i
) {
348 if (resource_name
== base::ASCIIToUTF16(kDataResources
[i
].name
))
349 return kDataResources
[i
].id
;
355 bool AutofillPopupControllerImpl::CanDelete(size_t index
) const {
356 // TODO(isherman): Native AddressBook suggestions on Mac and Android should
357 // not be considered to be deleteable.
358 int id
= identifiers_
[index
];
360 id
== WebAutofillClient::MenuItemIDAutocompleteEntry
||
361 id
== WebAutofillClient::MenuItemIDPasswordEntry
;
364 bool AutofillPopupControllerImpl::IsWarning(size_t index
) const {
365 return identifiers_
[index
] == WebAutofillClient::MenuItemIDWarningMessage
;
368 gfx::Rect
AutofillPopupControllerImpl::GetRowBounds(size_t index
) {
369 int top
= AutofillPopupView::kBorderThickness
;
370 for (size_t i
= 0; i
< index
; ++i
) {
371 top
+= GetRowHeightFromId(identifiers()[i
]);
375 AutofillPopupView::kBorderThickness
,
377 popup_bounds_
.width() - 2 * AutofillPopupView::kBorderThickness
,
378 GetRowHeightFromId(identifiers()[index
]));
381 void AutofillPopupControllerImpl::SetPopupBounds(const gfx::Rect
& bounds
) {
382 popup_bounds_
= bounds
;
383 UpdateBoundsAndRedrawPopup();
386 const gfx::Rect
& AutofillPopupControllerImpl::popup_bounds() const {
387 return popup_bounds_
;
390 gfx::NativeView
AutofillPopupControllerImpl::container_view() const {
391 return container_view_
;
394 const gfx::RectF
& AutofillPopupControllerImpl::element_bounds() const {
395 return element_bounds_
;
398 bool AutofillPopupControllerImpl::IsRTL() const {
399 return text_direction_
== base::i18n::RIGHT_TO_LEFT
;
402 bool AutofillPopupControllerImpl::hide_on_outside_click() const {
403 return hide_on_outside_click_
;
406 const std::vector
<base::string16
>& AutofillPopupControllerImpl::names() const {
410 const std::vector
<base::string16
>& AutofillPopupControllerImpl::subtexts()
415 const std::vector
<base::string16
>& AutofillPopupControllerImpl::icons() const {
419 const std::vector
<int>& AutofillPopupControllerImpl::identifiers() const {
423 #if !defined(OS_ANDROID)
424 const gfx::FontList
& AutofillPopupControllerImpl::GetNameFontListForRow(
425 size_t index
) const {
426 if (identifiers_
[index
] == WebAutofillClient::MenuItemIDWarningMessage
)
427 return warning_font_list_
;
429 return name_font_list_
;
432 const gfx::FontList
& AutofillPopupControllerImpl::subtext_font_list() const {
433 return subtext_font_list_
;
437 int AutofillPopupControllerImpl::selected_line() const {
438 return selected_line_
;
441 void AutofillPopupControllerImpl::set_hide_on_outside_click(
442 bool hide_on_outside_click
) {
443 hide_on_outside_click_
= hide_on_outside_click
;
446 void AutofillPopupControllerImpl::SetSelectedLine(int selected_line
) {
447 if (selected_line_
== selected_line
)
450 if (selected_line_
!= kNoSelection
&&
451 static_cast<size_t>(selected_line_
) < identifiers_
.size())
452 InvalidateRow(selected_line_
);
454 if (selected_line
!= kNoSelection
)
455 InvalidateRow(selected_line
);
457 selected_line_
= selected_line
;
459 if (selected_line_
!= kNoSelection
)
460 delegate_
->DidSelectSuggestion(identifiers_
[selected_line_
]);
462 delegate_
->ClearPreviewedForm();
465 void AutofillPopupControllerImpl::SelectNextLine() {
466 int new_selected_line
= selected_line_
+ 1;
468 // Skip over any lines that can't be selected.
469 while (static_cast<size_t>(new_selected_line
) < names_
.size() &&
470 !CanAccept(identifiers()[new_selected_line
])) {
474 if (new_selected_line
>= static_cast<int>(names_
.size()))
475 new_selected_line
= 0;
477 SetSelectedLine(new_selected_line
);
480 void AutofillPopupControllerImpl::SelectPreviousLine() {
481 int new_selected_line
= selected_line_
- 1;
483 // Skip over any lines that can't be selected.
484 while (new_selected_line
> kNoSelection
&&
485 !CanAccept(identifiers()[new_selected_line
])) {
489 if (new_selected_line
<= kNoSelection
)
490 new_selected_line
= names_
.size() - 1;
492 SetSelectedLine(new_selected_line
);
495 bool AutofillPopupControllerImpl::AcceptSelectedLine() {
496 if (selected_line_
== kNoSelection
)
499 DCHECK_GE(selected_line_
, 0);
500 DCHECK_LT(selected_line_
, static_cast<int>(names_
.size()));
502 if (!CanAccept(identifiers_
[selected_line_
]))
505 AcceptSuggestion(selected_line_
);
509 bool AutofillPopupControllerImpl::RemoveSelectedLine() {
510 if (selected_line_
== kNoSelection
)
513 DCHECK_GE(selected_line_
, 0);
514 DCHECK_LT(selected_line_
, static_cast<int>(names_
.size()));
516 if (!CanDelete(selected_line_
))
519 delegate_
->RemoveSuggestion(full_names_
[selected_line_
],
520 identifiers_
[selected_line_
]);
522 // Remove the deleted element.
523 names_
.erase(names_
.begin() + selected_line_
);
524 full_names_
.erase(full_names_
.begin() + selected_line_
);
525 subtexts_
.erase(subtexts_
.begin() + selected_line_
);
526 icons_
.erase(icons_
.begin() + selected_line_
);
527 identifiers_
.erase(identifiers_
.begin() + selected_line_
);
529 SetSelectedLine(kNoSelection
);
531 if (HasSuggestions()) {
532 delegate_
->ClearPreviewedForm();
533 UpdateBoundsAndRedrawPopup();
541 int AutofillPopupControllerImpl::LineFromY(int y
) {
542 int current_height
= AutofillPopupView::kBorderThickness
;
544 for (size_t i
= 0; i
< identifiers().size(); ++i
) {
545 current_height
+= GetRowHeightFromId(identifiers()[i
]);
547 if (y
<= current_height
)
551 // The y value goes beyond the popup so stop the selection at the last line.
552 return identifiers().size() - 1;
555 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier
) const {
556 if (identifier
== WebAutofillClient::MenuItemIDSeparator
)
557 return kSeparatorHeight
;
562 bool AutofillPopupControllerImpl::CanAccept(int id
) {
563 return id
!= WebAutofillClient::MenuItemIDSeparator
&&
564 id
!= WebAutofillClient::MenuItemIDWarningMessage
;
567 bool AutofillPopupControllerImpl::HasSuggestions() {
568 return identifiers_
.size() != 0 &&
569 (identifiers_
[0] > 0 ||
571 WebAutofillClient::MenuItemIDAutocompleteEntry
||
572 identifiers_
[0] == WebAutofillClient::MenuItemIDPasswordEntry
||
573 identifiers_
[0] == WebAutofillClient::MenuItemIDDataListEntry
);
576 void AutofillPopupControllerImpl::SetValues(
577 const std::vector
<base::string16
>& names
,
578 const std::vector
<base::string16
>& subtexts
,
579 const std::vector
<base::string16
>& icons
,
580 const std::vector
<int>& identifiers
) {
583 subtexts_
= subtexts
;
585 identifiers_
= identifiers
;
588 void AutofillPopupControllerImpl::ShowView() {
592 void AutofillPopupControllerImpl::InvalidateRow(size_t row
) {
594 DCHECK(row
< identifiers_
.size());
595 view_
->InvalidateRow(row
);
598 #if !defined(OS_ANDROID)
599 int AutofillPopupControllerImpl::GetDesiredPopupWidth() const {
600 int popup_width
= RoundedElementBounds().width();
601 DCHECK_EQ(names().size(), subtexts().size());
602 for (size_t i
= 0; i
< names().size(); ++i
) {
604 gfx::GetStringWidth(names()[i
], name_font_list_
) +
605 gfx::GetStringWidth(subtexts()[i
], subtext_font_list_
) +
606 RowWidthWithoutText(i
);
608 popup_width
= std::max(popup_width
, row_size
);
614 int AutofillPopupControllerImpl::GetDesiredPopupHeight() const {
615 int popup_height
= 2 * AutofillPopupView::kBorderThickness
;
617 for (size_t i
= 0; i
< identifiers().size(); ++i
) {
618 popup_height
+= GetRowHeightFromId(identifiers()[i
]);
624 int AutofillPopupControllerImpl::RowWidthWithoutText(int row
) const {
625 int row_size
= kEndPadding
;
627 if (!subtexts_
[row
].empty())
628 row_size
+= kNamePadding
;
630 // Add the Autofill icon size, if required.
631 if (!icons_
[row
].empty()) {
632 int icon_width
= ui::ResourceBundle::GetSharedInstance().GetImageNamed(
633 GetIconResourceID(icons_
[row
])).Width();
634 row_size
+= icon_width
+ kIconPadding
;
637 // Add the padding at the end.
638 row_size
+= kEndPadding
;
640 // Add room for the popup border.
641 row_size
+= 2 * AutofillPopupView::kBorderThickness
;
646 void AutofillPopupControllerImpl::UpdatePopupBounds() {
647 int popup_required_width
= GetDesiredPopupWidth();
648 int popup_height
= GetDesiredPopupHeight();
649 // This is the top left point of the popup if the popup is above the element
650 // and grows to the left (since that is the highest and furthest left the
652 gfx::Point top_left_corner_of_popup
= RoundedElementBounds().origin() +
653 gfx::Vector2d(RoundedElementBounds().width() - popup_required_width
,
656 // This is the bottom right point of the popup if the popup is below the
657 // element and grows to the right (since the is the lowest and furthest right
658 // the popup could go).
659 gfx::Point bottom_right_corner_of_popup
= RoundedElementBounds().origin() +
660 gfx::Vector2d(popup_required_width
,
661 RoundedElementBounds().height() + popup_height
);
663 gfx::Display top_left_display
= GetDisplayNearestPoint(
664 top_left_corner_of_popup
);
665 gfx::Display bottom_right_display
= GetDisplayNearestPoint(
666 bottom_right_corner_of_popup
);
668 std::pair
<int, int> popup_x_and_width
= CalculatePopupXAndWidth(
669 top_left_display
, bottom_right_display
, popup_required_width
);
670 std::pair
<int, int> popup_y_and_height
= CalculatePopupYAndHeight(
671 top_left_display
, bottom_right_display
, popup_height
);
673 popup_bounds_
= gfx::Rect(popup_x_and_width
.first
,
674 popup_y_and_height
.first
,
675 popup_x_and_width
.second
,
676 popup_y_and_height
.second
);
678 #endif // !defined(OS_ANDROID)
680 WeakPtr
<AutofillPopupControllerImpl
> AutofillPopupControllerImpl::GetWeakPtr() {
681 return weak_ptr_factory_
.GetWeakPtr();
684 void AutofillPopupControllerImpl::ClearState() {
685 // Don't clear view_, because otherwise the popup will have to get regenerated
686 // and this will cause flickering.
688 popup_bounds_
= gfx::Rect();
693 identifiers_
.clear();
696 selected_line_
= kNoSelection
;
699 const gfx::Rect
AutofillPopupControllerImpl::RoundedElementBounds() const {
700 return gfx::ToEnclosingRect(element_bounds_
);
703 gfx::Display
AutofillPopupControllerImpl::GetDisplayNearestPoint(
704 const gfx::Point
& point
) const {
705 return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint(
709 std::pair
<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth(
710 const gfx::Display
& left_display
,
711 const gfx::Display
& right_display
,
712 int popup_required_width
) const {
713 int leftmost_display_x
= left_display
.bounds().x();
714 int rightmost_display_x
=
715 right_display
.GetSizeInPixel().width() + right_display
.bounds().x();
717 // Calculate the start coordinates for the popup if it is growing right or
718 // the end position if it is growing to the left, capped to screen space.
719 int right_growth_start
= std::max(leftmost_display_x
,
720 std::min(rightmost_display_x
,
721 RoundedElementBounds().x()));
722 int left_growth_end
= std::max(leftmost_display_x
,
723 std::min(rightmost_display_x
,
724 RoundedElementBounds().right()));
726 int right_available
= rightmost_display_x
- right_growth_start
;
727 int left_available
= left_growth_end
- leftmost_display_x
;
729 int popup_width
= std::min(popup_required_width
,
730 std::max(right_available
, left_available
));
732 // If there is enough space for the popup on the right, show it there,
733 // otherwise choose the larger size.
734 if (right_available
>= popup_width
|| right_available
>= left_available
)
735 return std::make_pair(right_growth_start
, popup_width
);
737 return std::make_pair(left_growth_end
- popup_width
, popup_width
);
740 std::pair
<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight(
741 const gfx::Display
& top_display
,
742 const gfx::Display
& bottom_display
,
743 int popup_required_height
) const {
744 int topmost_display_y
= top_display
.bounds().y();
745 int bottommost_display_y
=
746 bottom_display
.GetSizeInPixel().height() + bottom_display
.bounds().y();
748 // Calculate the start coordinates for the popup if it is growing down or
749 // the end position if it is growing up, capped to screen space.
750 int top_growth_end
= std::max(topmost_display_y
,
751 std::min(bottommost_display_y
,
752 RoundedElementBounds().y()));
753 int bottom_growth_start
= std::max(topmost_display_y
,
754 std::min(bottommost_display_y
, RoundedElementBounds().bottom()));
756 int top_available
= bottom_growth_start
- topmost_display_y
;
757 int bottom_available
= bottommost_display_y
- top_growth_end
;
759 // TODO(csharp): Restrict the popup height to what is available.
760 if (bottom_available
>= popup_required_height
||
761 bottom_available
>= top_available
) {
762 // The popup can appear below the field.
763 return std::make_pair(bottom_growth_start
, popup_required_height
);
765 // The popup must appear above the field.
766 return std::make_pair(top_growth_end
- popup_required_height
,
767 popup_required_height
);
771 } // namespace autofill