Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / autofill / autofill_popup_controller_impl.cc
blob8ffedad8551c8a53439ecb2192615949503195ee
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"
7 #include <algorithm>
8 #include <utility>
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"
28 using base::WeakPtr;
29 using blink::WebAutofillClient;
31 namespace autofill {
32 namespace {
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;
50 #endif
52 struct DataResource {
53 const char* name;
54 int id;
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 },
67 } // namespace
69 // static
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();
83 return previous;
86 if (previous.get())
87 previous->Hide();
89 AutofillPopupControllerImpl* controller =
90 new AutofillPopupControllerImpl(
91 delegate, web_contents, container_view, element_bounds,
92 text_direction);
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)
102 : view_(NULL),
103 delegate_(delegate),
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) {
114 ClearState();
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_;
121 #else
122 warning_font_list_ = name_font_list_.DeriveFontListWithSizeDeltaAndStyle(
123 0, gfx::Font::ITALIC);
124 #endif
125 #endif
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.
140 UpdatePopupBounds();
141 int popup_width = popup_bounds().width();
143 // Elide the name and subtext strings so that the popup fits in the available
144 // space.
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
151 // a separator line.
152 if (total_text_length == 0)
153 continue;
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),
161 name_size,
162 gfx::ELIDE_AT_END);
164 int subtext_size = available_width * subtext_width / total_text_length;
165 subtexts_[i] = gfx::ElideText(subtexts_[i],
166 subtext_font_list(),
167 subtext_size,
168 gfx::ELIDE_AT_END);
170 #endif
172 if (!view_) {
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.
177 if (!view_) {
178 Hide();
179 return;
182 ShowView();
183 } else {
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
210 // is one).
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();
223 else
224 Hide();
226 return;
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(),
246 values.size(),
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;
261 if (delegate_.get())
262 delegate_->OnPopupHidden();
264 if (view_)
265 view_->Hide();
267 delete this;
270 void AutofillPopupControllerImpl::ViewDestroyed() {
271 // The view has already been destroyed so clear the reference to it.
272 view_ = NULL;
274 Hide();
277 bool AutofillPopupControllerImpl::HandleKeyPressEvent(
278 const content::NativeWebKeyboardEvent& event) {
279 switch (event.windowsKeyCode) {
280 case ui::VKEY_UP:
281 SelectPreviousLine();
282 return true;
283 case ui::VKEY_DOWN:
284 SelectNextLine();
285 return true;
286 case ui::VKEY_PRIOR: // Page up.
287 SetSelectedLine(0);
288 return true;
289 case ui::VKEY_NEXT: // Page down.
290 SetSelectedLine(names().size() - 1);
291 return true;
292 case ui::VKEY_ESCAPE:
293 Hide();
294 return true;
295 case ui::VKEY_DELETE:
296 return (event.modifiers & content::NativeWebKeyboardEvent::ShiftKey) &&
297 RemoveSelectedLine();
298 case ui::VKEY_TAB:
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
301 // location.
302 AcceptSelectedLine();
303 return false;
304 case ui::VKEY_RETURN:
305 return AcceptSelectedLine();
306 default:
307 return false;
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.
317 UpdatePopupBounds();
318 #endif
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;
352 return -1;
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];
359 return id > 0 ||
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]);
374 return gfx::Rect(
375 AutofillPopupView::kBorderThickness,
376 top,
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 {
407 return names_;
410 const std::vector<base::string16>& AutofillPopupControllerImpl::subtexts()
411 const {
412 return subtexts_;
415 const std::vector<base::string16>& AutofillPopupControllerImpl::icons() const {
416 return icons_;
419 const std::vector<int>& AutofillPopupControllerImpl::identifiers() const {
420 return identifiers_;
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_;
435 #endif
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)
448 return;
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_]);
461 else
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])) {
471 ++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])) {
486 --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)
497 return false;
499 DCHECK_GE(selected_line_, 0);
500 DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
502 if (!CanAccept(identifiers_[selected_line_]))
503 return false;
505 AcceptSuggestion(selected_line_);
506 return true;
509 bool AutofillPopupControllerImpl::RemoveSelectedLine() {
510 if (selected_line_ == kNoSelection)
511 return false;
513 DCHECK_GE(selected_line_, 0);
514 DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
516 if (!CanDelete(selected_line_))
517 return false;
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();
534 } else {
535 Hide();
538 return true;
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)
548 return i;
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;
559 return kRowHeight;
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 ||
570 identifiers_[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) {
581 names_ = names;
582 full_names_ = names;
583 subtexts_ = subtexts;
584 icons_ = icons;
585 identifiers_ = identifiers;
588 void AutofillPopupControllerImpl::ShowView() {
589 view_->Show();
592 void AutofillPopupControllerImpl::InvalidateRow(size_t row) {
593 DCHECK(0 <= 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) {
603 int row_size =
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);
611 return popup_width;
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]);
621 return popup_height;
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;
643 return row_size;
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
651 // popup go could).
652 gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() +
653 gfx::Vector2d(RoundedElementBounds().width() - popup_required_width,
654 -popup_height);
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();
690 names_.clear();
691 subtexts_.clear();
692 icons_.clear();
693 identifiers_.clear();
694 full_names_.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(
706 point);
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);
736 else
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);
764 } else {
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