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