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