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 27 using base::WeakPtr; 28 using blink::WebAutofillClient; 29 30 namespace autofill { 31 namespace { 32 33 // Used to indicate that no line is currently selected by the user. 34 const int kNoSelection = -1; 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 #if !defined(OS_ANDROID) 43 // Size difference between name and subtext in pixels. 44 const int kLabelFontSizeDelta = -2; 45 46 const size_t kNamePadding = AutofillPopupView::kNamePadding; 47 const size_t kIconPadding = AutofillPopupView::kIconPadding; 48 const size_t kEndPadding = AutofillPopupView::kEndPadding; 49 #endif 50 51 struct DataResource { 52 const char* name; 53 int id; 54 }; 55 56 const 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 69 WeakPtr<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 95 AutofillPopupControllerImpl::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 125 AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {} 126 127 void AutofillPopupControllerImpl::Show( 128 const std::vector<base::string16>& names, 129 const std::vector<base::string16>& subtexts, 130 const std::vector<base::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 193 void 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 249 void 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 267 void AutofillPopupControllerImpl::ViewDestroyed() { 268 // The view has already been destroyed so clear the reference to it. 269 view_ = NULL; 270 271 Hide(); 272 } 273 274 bool 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 308 void 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 320 void AutofillPopupControllerImpl::LineSelectedAtPoint(int x, int y) { 321 SetSelectedLine(LineFromY(y)); 322 } 323 324 void AutofillPopupControllerImpl::LineAcceptedAtPoint(int x, int y) { 325 LineSelectedAtPoint(x, y); 326 AcceptSelectedLine(); 327 } 328 329 void AutofillPopupControllerImpl::SelectionCleared() { 330 SetSelectedLine(kNoSelection); 331 } 332 333 bool AutofillPopupControllerImpl::ShouldRepostEvent( 334 const ui::MouseEvent& event) { 335 return delegate_->ShouldRepostEvent(event); 336 } 337 338 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) { 339 delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]); 340 } 341 342 int 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 352 bool 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 361 bool AutofillPopupControllerImpl::IsWarning(size_t index) const { 362 return identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage; 363 } 364 365 gfx::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 378 void AutofillPopupControllerImpl::SetPopupBounds(const gfx::Rect& bounds) { 379 popup_bounds_ = bounds; 380 UpdateBoundsAndRedrawPopup(); 381 } 382 383 const gfx::Rect& AutofillPopupControllerImpl::popup_bounds() const { 384 return popup_bounds_; 385 } 386 387 gfx::NativeView AutofillPopupControllerImpl::container_view() const { 388 return container_view_; 389 } 390 391 const gfx::RectF& AutofillPopupControllerImpl::element_bounds() const { 392 return element_bounds_; 393 } 394 395 bool AutofillPopupControllerImpl::IsRTL() const { 396 return text_direction_ == base::i18n::RIGHT_TO_LEFT; 397 } 398 399 bool AutofillPopupControllerImpl::hide_on_outside_click() const { 400 return hide_on_outside_click_; 401 } 402 403 const std::vector<base::string16>& AutofillPopupControllerImpl::names() const { 404 return names_; 405 } 406 407 const std::vector<base::string16>& AutofillPopupControllerImpl::subtexts() 408 const { 409 return subtexts_; 410 } 411 412 const std::vector<base::string16>& AutofillPopupControllerImpl::icons() const { 413 return icons_; 414 } 415 416 const std::vector<int>& AutofillPopupControllerImpl::identifiers() const { 417 return identifiers_; 418 } 419 420 #if !defined(OS_ANDROID) 421 const gfx::Font& AutofillPopupControllerImpl::GetNameFontForRow(size_t index) 422 const { 423 if (identifiers_[index] == WebAutofillClient::MenuItemIDWarningMessage) 424 return warning_font_; 425 426 return name_font_; 427 } 428 429 const gfx::Font& AutofillPopupControllerImpl::subtext_font() const { 430 return subtext_font_; 431 } 432 #endif 433 434 int AutofillPopupControllerImpl::selected_line() const { 435 return selected_line_; 436 } 437 438 void AutofillPopupControllerImpl::set_hide_on_outside_click( 439 bool hide_on_outside_click) { 440 hide_on_outside_click_ = hide_on_outside_click; 441 } 442 443 void AutofillPopupControllerImpl::SetSelectedLine(int selected_line) { 444 if (selected_line_ == selected_line) 445 return; 446 447 if (selected_line_ != kNoSelection && 448 static_cast<size_t>(selected_line_) < identifiers_.size()) 449 InvalidateRow(selected_line_); 450 451 if (selected_line != kNoSelection) 452 InvalidateRow(selected_line); 453 454 selected_line_ = selected_line; 455 456 if (selected_line_ != kNoSelection) 457 delegate_->DidSelectSuggestion(identifiers_[selected_line_]); 458 else 459 delegate_->ClearPreviewedForm(); 460 } 461 462 void AutofillPopupControllerImpl::SelectNextLine() { 463 int new_selected_line = selected_line_ + 1; 464 465 // Skip over any lines that can't be selected. 466 while (static_cast<size_t>(new_selected_line) < names_.size() && 467 !CanAccept(identifiers()[new_selected_line])) { 468 ++new_selected_line; 469 } 470 471 if (new_selected_line >= static_cast<int>(names_.size())) 472 new_selected_line = 0; 473 474 SetSelectedLine(new_selected_line); 475 } 476 477 void AutofillPopupControllerImpl::SelectPreviousLine() { 478 int new_selected_line = selected_line_ - 1; 479 480 // Skip over any lines that can't be selected. 481 while (new_selected_line > kNoSelection && 482 !CanAccept(identifiers()[new_selected_line])) { 483 --new_selected_line; 484 } 485 486 if (new_selected_line <= kNoSelection) 487 new_selected_line = names_.size() - 1; 488 489 SetSelectedLine(new_selected_line); 490 } 491 492 bool AutofillPopupControllerImpl::AcceptSelectedLine() { 493 if (selected_line_ == kNoSelection) 494 return false; 495 496 DCHECK_GE(selected_line_, 0); 497 DCHECK_LT(selected_line_, static_cast<int>(names_.size())); 498 499 if (!CanAccept(identifiers_[selected_line_])) 500 return false; 501 502 AcceptSuggestion(selected_line_); 503 return true; 504 } 505 506 bool AutofillPopupControllerImpl::RemoveSelectedLine() { 507 if (selected_line_ == kNoSelection) 508 return false; 509 510 DCHECK_GE(selected_line_, 0); 511 DCHECK_LT(selected_line_, static_cast<int>(names_.size())); 512 513 if (!CanDelete(selected_line_)) 514 return false; 515 516 delegate_->RemoveSuggestion(full_names_[selected_line_], 517 identifiers_[selected_line_]); 518 519 // Remove the deleted element. 520 names_.erase(names_.begin() + selected_line_); 521 full_names_.erase(full_names_.begin() + selected_line_); 522 subtexts_.erase(subtexts_.begin() + selected_line_); 523 icons_.erase(icons_.begin() + selected_line_); 524 identifiers_.erase(identifiers_.begin() + selected_line_); 525 526 SetSelectedLine(kNoSelection); 527 528 if (HasSuggestions()) { 529 delegate_->ClearPreviewedForm(); 530 UpdateBoundsAndRedrawPopup(); 531 } else { 532 Hide(); 533 } 534 535 return true; 536 } 537 538 int AutofillPopupControllerImpl::LineFromY(int y) { 539 int current_height = AutofillPopupView::kBorderThickness; 540 541 for (size_t i = 0; i < identifiers().size(); ++i) { 542 current_height += GetRowHeightFromId(identifiers()[i]); 543 544 if (y <= current_height) 545 return i; 546 } 547 548 // The y value goes beyond the popup so stop the selection at the last line. 549 return identifiers().size() - 1; 550 } 551 552 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const { 553 if (identifier == WebAutofillClient::MenuItemIDSeparator) 554 return kSeparatorHeight; 555 556 return kRowHeight; 557 } 558 559 bool AutofillPopupControllerImpl::CanAccept(int id) { 560 return id != WebAutofillClient::MenuItemIDSeparator && 561 id != WebAutofillClient::MenuItemIDWarningMessage; 562 } 563 564 bool AutofillPopupControllerImpl::HasSuggestions() { 565 return identifiers_.size() != 0 && 566 (identifiers_[0] > 0 || 567 identifiers_[0] == 568 WebAutofillClient::MenuItemIDAutocompleteEntry || 569 identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry || 570 identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry); 571 } 572 573 void AutofillPopupControllerImpl::SetValues( 574 const std::vector<base::string16>& names, 575 const std::vector<base::string16>& subtexts, 576 const std::vector<base::string16>& icons, 577 const std::vector<int>& identifiers) { 578 names_ = names; 579 full_names_ = names; 580 subtexts_ = subtexts; 581 icons_ = icons; 582 identifiers_ = identifiers; 583 } 584 585 void AutofillPopupControllerImpl::ShowView() { 586 view_->Show(); 587 } 588 589 void AutofillPopupControllerImpl::InvalidateRow(size_t row) { 590 DCHECK(0 <= row); 591 DCHECK(row < identifiers_.size()); 592 view_->InvalidateRow(row); 593 } 594 595 #if !defined(OS_ANDROID) 596 int AutofillPopupControllerImpl::GetDesiredPopupWidth() const { 597 if (!name_font_.platform_font() || !subtext_font_.platform_font()) { 598 // We can't calculate the size of the popup if the fonts 599 // aren't present. 600 return 0; 601 } 602 603 int popup_width = RoundedElementBounds().width(); 604 DCHECK_EQ(names().size(), subtexts().size()); 605 for (size_t i = 0; i < names().size(); ++i) { 606 int row_size = name_font_.GetStringWidth(names()[i]) + 607 subtext_font_.GetStringWidth(subtexts()[i]) + 608 RowWidthWithoutText(i); 609 610 popup_width = std::max(popup_width, row_size); 611 } 612 613 return popup_width; 614 } 615 616 int AutofillPopupControllerImpl::GetDesiredPopupHeight() const { 617 int popup_height = 2 * AutofillPopupView::kBorderThickness; 618 619 for (size_t i = 0; i < identifiers().size(); ++i) { 620 popup_height += GetRowHeightFromId(identifiers()[i]); 621 } 622 623 return popup_height; 624 } 625 626 int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const { 627 int row_size = kEndPadding; 628 629 if (!subtexts_[row].empty()) 630 row_size += kNamePadding; 631 632 // Add the Autofill icon size, if required. 633 if (!icons_[row].empty()) { 634 int icon_width = ui::ResourceBundle::GetSharedInstance().GetImageNamed( 635 GetIconResourceID(icons_[row])).Width(); 636 row_size += icon_width + kIconPadding; 637 } 638 639 // Add the padding at the end. 640 row_size += kEndPadding; 641 642 // Add room for the popup border. 643 row_size += 2 * AutofillPopupView::kBorderThickness; 644 645 return row_size; 646 } 647 648 void AutofillPopupControllerImpl::UpdatePopupBounds() { 649 int popup_required_width = GetDesiredPopupWidth(); 650 int popup_height = GetDesiredPopupHeight(); 651 // This is the top left point of the popup if the popup is above the element 652 // and grows to the left (since that is the highest and furthest left the 653 // popup go could). 654 gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() + 655 gfx::Vector2d(RoundedElementBounds().width() - popup_required_width, 656 -popup_height); 657 658 // This is the bottom right point of the popup if the popup is below the 659 // element and grows to the right (since the is the lowest and furthest right 660 // the popup could go). 661 gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() + 662 gfx::Vector2d(popup_required_width, 663 RoundedElementBounds().height() + popup_height); 664 665 gfx::Display top_left_display = GetDisplayNearestPoint( 666 top_left_corner_of_popup); 667 gfx::Display bottom_right_display = GetDisplayNearestPoint( 668 bottom_right_corner_of_popup); 669 670 std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth( 671 top_left_display, bottom_right_display, popup_required_width); 672 std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight( 673 top_left_display, bottom_right_display, popup_height); 674 675 popup_bounds_ = gfx::Rect(popup_x_and_width.first, 676 popup_y_and_height.first, 677 popup_x_and_width.second, 678 popup_y_and_height.second); 679 } 680 #endif // !defined(OS_ANDROID) 681 682 WeakPtr<AutofillPopupControllerImpl> AutofillPopupControllerImpl::GetWeakPtr() { 683 return weak_ptr_factory_.GetWeakPtr(); 684 } 685 686 void AutofillPopupControllerImpl::ClearState() { 687 // Don't clear view_, because otherwise the popup will have to get regenerated 688 // and this will cause flickering. 689 690 popup_bounds_ = gfx::Rect(); 691 692 names_.clear(); 693 subtexts_.clear(); 694 icons_.clear(); 695 identifiers_.clear(); 696 full_names_.clear(); 697 698 selected_line_ = kNoSelection; 699 } 700 701 const gfx::Rect AutofillPopupControllerImpl::RoundedElementBounds() const { 702 return gfx::ToEnclosingRect(element_bounds_); 703 } 704 705 gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint( 706 const gfx::Point& point) const { 707 return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint( 708 point); 709 } 710 711 std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth( 712 const gfx::Display& left_display, 713 const gfx::Display& right_display, 714 int popup_required_width) const { 715 int leftmost_display_x = left_display.bounds().x(); 716 int rightmost_display_x = 717 right_display.GetSizeInPixel().width() + right_display.bounds().x(); 718 719 // Calculate the start coordinates for the popup if it is growing right or 720 // the end position if it is growing to the left, capped to screen space. 721 int right_growth_start = std::max(leftmost_display_x, 722 std::min(rightmost_display_x, 723 RoundedElementBounds().x())); 724 int left_growth_end = std::max(leftmost_display_x, 725 std::min(rightmost_display_x, 726 RoundedElementBounds().right())); 727 728 int right_available = rightmost_display_x - right_growth_start; 729 int left_available = left_growth_end - leftmost_display_x; 730 731 int popup_width = std::min(popup_required_width, 732 std::max(right_available, left_available)); 733 734 // If there is enough space for the popup on the right, show it there, 735 // otherwise choose the larger size. 736 if (right_available >= popup_width || right_available >= left_available) 737 return std::make_pair(right_growth_start, popup_width); 738 else 739 return std::make_pair(left_growth_end - popup_width, popup_width); 740 } 741 742 std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight( 743 const gfx::Display& top_display, 744 const gfx::Display& bottom_display, 745 int popup_required_height) const { 746 int topmost_display_y = top_display.bounds().y(); 747 int bottommost_display_y = 748 bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y(); 749 750 // Calculate the start coordinates for the popup if it is growing down or 751 // the end position if it is growing up, capped to screen space. 752 int top_growth_end = std::max(topmost_display_y, 753 std::min(bottommost_display_y, 754 RoundedElementBounds().y())); 755 int bottom_growth_start = std::max(topmost_display_y, 756 std::min(bottommost_display_y, RoundedElementBounds().bottom())); 757 758 int top_available = bottom_growth_start - topmost_display_y; 759 int bottom_available = bottommost_display_y - top_growth_end; 760 761 // TODO(csharp): Restrict the popup height to what is available. 762 if (bottom_available >= popup_required_height || 763 bottom_available >= top_available) { 764 // The popup can appear below the field. 765 return std::make_pair(bottom_growth_start, popup_required_height); 766 } else { 767 // The popup must appear above the field. 768 return std::make_pair(top_growth_end - popup_required_height, 769 popup_required_height); 770 } 771 } 772 773 } // namespace autofill 774