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