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 "base/memory/scoped_ptr.h" 6 #include "base/memory/weak_ptr.h" 7 #include "base/prefs/pref_service.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" 10 #include "chrome/browser/ui/autofill/autofill_popup_view.h" 11 #include "chrome/browser/ui/autofill/popup_constants.h" 12 #include "chrome/browser/ui/autofill/test_popup_controller_common.h" 13 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 14 #include "chrome/test/base/testing_profile.h" 15 #include "components/autofill/content/browser/content_autofill_driver.h" 16 #include "components/autofill/core/browser/autofill_external_delegate.h" 17 #include "components/autofill/core/browser/autofill_manager.h" 18 #include "components/autofill/core/browser/autofill_test_utils.h" 19 #include "components/autofill/core/browser/popup_item_ids.h" 20 #include "components/autofill/core/browser/test_autofill_client.h" 21 #include "components/autofill/core/browser/test_autofill_external_delegate.h" 22 #include "grit/components_scaled_resources.h" 23 #include "testing/gmock/include/gmock/gmock.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 #include "ui/base/resource/resource_bundle.h" 26 #include "ui/gfx/display.h" 27 #include "ui/gfx/rect.h" 28 #include "ui/gfx/text_utils.h" 29 30 using ::testing::_; 31 using ::testing::AtLeast; 32 using ::testing::NiceMock; 33 using base::ASCIIToUTF16; 34 using base::WeakPtr; 35 36 namespace autofill { 37 namespace { 38 39 class MockAutofillExternalDelegate : public AutofillExternalDelegate { 40 public: 41 MockAutofillExternalDelegate(AutofillManager* autofill_manager, 42 AutofillDriver* autofill_driver) 43 : AutofillExternalDelegate(autofill_manager, autofill_driver) {} 44 virtual ~MockAutofillExternalDelegate() {} 45 46 virtual void DidSelectSuggestion(const base::string16& value, 47 int identifier) OVERRIDE {} 48 virtual void RemoveSuggestion(const base::string16& value, 49 int identifier) OVERRIDE {} 50 virtual void ClearPreviewedForm() OVERRIDE {} 51 base::WeakPtr<AutofillExternalDelegate> GetWeakPtr() { 52 return AutofillExternalDelegate::GetWeakPtr(); 53 } 54 }; 55 56 class MockAutofillClient : public autofill::TestAutofillClient { 57 public: 58 MockAutofillClient() : prefs_(autofill::test::PrefServiceForTesting()) {} 59 virtual ~MockAutofillClient() {} 60 61 virtual PrefService* GetPrefs() OVERRIDE { return prefs_.get(); } 62 63 private: 64 scoped_ptr<PrefService> prefs_; 65 66 DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); 67 }; 68 69 class TestAutofillPopupController : public AutofillPopupControllerImpl { 70 public: 71 explicit TestAutofillPopupController( 72 base::WeakPtr<AutofillExternalDelegate> external_delegate, 73 const gfx::RectF& element_bounds) 74 : AutofillPopupControllerImpl( 75 external_delegate, NULL, NULL, element_bounds, 76 base::i18n::UNKNOWN_DIRECTION), 77 test_controller_common_(new TestPopupControllerCommon(element_bounds)) { 78 controller_common_.reset(test_controller_common_); 79 } 80 virtual ~TestAutofillPopupController() {} 81 82 void set_display(const gfx::Display& display) { 83 test_controller_common_->set_display(display); 84 } 85 86 // Making protected functions public for testing 87 using AutofillPopupControllerImpl::SetPopupBounds; 88 using AutofillPopupControllerImpl::names; 89 using AutofillPopupControllerImpl::subtexts; 90 using AutofillPopupControllerImpl::identifiers; 91 using AutofillPopupControllerImpl::selected_line; 92 using AutofillPopupControllerImpl::SetSelectedLine; 93 using AutofillPopupControllerImpl::SelectNextLine; 94 using AutofillPopupControllerImpl::SelectPreviousLine; 95 using AutofillPopupControllerImpl::RemoveSelectedLine; 96 using AutofillPopupControllerImpl::popup_bounds; 97 using AutofillPopupControllerImpl::element_bounds; 98 #if !defined(OS_ANDROID) 99 using AutofillPopupControllerImpl::GetNameFontListForRow; 100 using AutofillPopupControllerImpl::subtext_font_list; 101 using AutofillPopupControllerImpl::RowWidthWithoutText; 102 #endif 103 using AutofillPopupControllerImpl::SetValues; 104 using AutofillPopupControllerImpl::GetDesiredPopupWidth; 105 using AutofillPopupControllerImpl::GetDesiredPopupHeight; 106 using AutofillPopupControllerImpl::GetWeakPtr; 107 MOCK_METHOD1(InvalidateRow, void(size_t)); 108 MOCK_METHOD0(UpdateBoundsAndRedrawPopup, void()); 109 MOCK_METHOD0(Hide, void()); 110 111 void DoHide() { 112 AutofillPopupControllerImpl::Hide(); 113 } 114 115 private: 116 virtual void ShowView() OVERRIDE {} 117 118 TestPopupControllerCommon* test_controller_common_; 119 }; 120 121 } // namespace 122 123 class AutofillPopupControllerUnitTest : public ChromeRenderViewHostTestHarness { 124 public: 125 AutofillPopupControllerUnitTest() 126 : autofill_client_(new MockAutofillClient()), 127 autofill_popup_controller_(NULL) {} 128 virtual ~AutofillPopupControllerUnitTest() {} 129 130 virtual void SetUp() OVERRIDE { 131 ChromeRenderViewHostTestHarness::SetUp(); 132 133 ContentAutofillDriver::CreateForWebContentsAndDelegate( 134 web_contents(), 135 autofill_client_.get(), 136 "en-US", 137 AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER); 138 ContentAutofillDriver* driver = 139 ContentAutofillDriver::FromWebContents(web_contents()); 140 external_delegate_.reset( 141 new NiceMock<MockAutofillExternalDelegate>( 142 driver->autofill_manager(), 143 driver)); 144 145 autofill_popup_controller_ = 146 new testing::NiceMock<TestAutofillPopupController>( 147 external_delegate_->GetWeakPtr(),gfx::Rect()); 148 } 149 150 virtual void TearDown() OVERRIDE { 151 // This will make sure the controller and the view (if any) are both 152 // cleaned up. 153 if (autofill_popup_controller_) 154 autofill_popup_controller_->DoHide(); 155 156 external_delegate_.reset(); 157 ChromeRenderViewHostTestHarness::TearDown(); 158 } 159 160 TestAutofillPopupController* popup_controller() { 161 return autofill_popup_controller_; 162 } 163 164 MockAutofillExternalDelegate* delegate() { 165 return external_delegate_.get(); 166 } 167 168 protected: 169 scoped_ptr<MockAutofillClient> autofill_client_; 170 scoped_ptr<NiceMock<MockAutofillExternalDelegate> > external_delegate_; 171 testing::NiceMock<TestAutofillPopupController>* autofill_popup_controller_; 172 }; 173 174 TEST_F(AutofillPopupControllerUnitTest, SetBounds) { 175 // Ensure the popup size can be set and causes a redraw. 176 gfx::Rect popup_bounds(10, 10, 100, 100); 177 178 EXPECT_CALL(*autofill_popup_controller_, 179 UpdateBoundsAndRedrawPopup()); 180 181 popup_controller()->SetPopupBounds(popup_bounds); 182 183 EXPECT_EQ(popup_bounds, popup_controller()->popup_bounds()); 184 } 185 186 TEST_F(AutofillPopupControllerUnitTest, ChangeSelectedLine) { 187 // Set up the popup. 188 std::vector<base::string16> names(2, base::string16()); 189 std::vector<int> autofill_ids(2, 0); 190 autofill_popup_controller_->Show(names, names, names, autofill_ids); 191 192 EXPECT_LT(autofill_popup_controller_->selected_line(), 0); 193 // Check that there are at least 2 values so that the first and last selection 194 // are different. 195 EXPECT_GE(2, 196 static_cast<int>(autofill_popup_controller_->subtexts().size())); 197 198 // Test wrapping before the front. 199 autofill_popup_controller_->SelectPreviousLine(); 200 EXPECT_EQ(static_cast<int>( 201 autofill_popup_controller_->subtexts().size() - 1), 202 autofill_popup_controller_->selected_line()); 203 204 // Test wrapping after the end. 205 autofill_popup_controller_->SelectNextLine(); 206 EXPECT_EQ(0, autofill_popup_controller_->selected_line()); 207 } 208 209 TEST_F(AutofillPopupControllerUnitTest, RedrawSelectedLine) { 210 // Set up the popup. 211 std::vector<base::string16> names(2, base::string16()); 212 std::vector<int> autofill_ids(2, 0); 213 autofill_popup_controller_->Show(names, names, names, autofill_ids); 214 215 // Make sure that when a new line is selected, it is invalidated so it can 216 // be updated to show it is selected. 217 int selected_line = 0; 218 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line)); 219 autofill_popup_controller_->SetSelectedLine(selected_line); 220 221 // Ensure that the row isn't invalidated if it didn't change. 222 EXPECT_CALL(*autofill_popup_controller_, 223 InvalidateRow(selected_line)).Times(0); 224 autofill_popup_controller_->SetSelectedLine(selected_line); 225 226 // Change back to no selection. 227 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(selected_line)); 228 autofill_popup_controller_->SetSelectedLine(-1); 229 } 230 231 TEST_F(AutofillPopupControllerUnitTest, RemoveLine) { 232 // Set up the popup. 233 std::vector<base::string16> names(3, base::string16()); 234 std::vector<int> autofill_ids; 235 autofill_ids.push_back(1); 236 autofill_ids.push_back(1); 237 autofill_ids.push_back(POPUP_ITEM_ID_AUTOFILL_OPTIONS); 238 autofill_popup_controller_->Show(names, names, names, autofill_ids); 239 240 // Generate a popup, so it can be hidden later. It doesn't matter what the 241 // external_delegate thinks is being shown in the process, since we are just 242 // testing the popup here. 243 autofill::GenerateTestAutofillPopup(external_delegate_.get()); 244 245 // No line is selected so the removal should fail. 246 EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine()); 247 248 // Try to remove the last entry and ensure it fails (it is an option). 249 autofill_popup_controller_->SetSelectedLine( 250 autofill_popup_controller_->subtexts().size() - 1); 251 EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine()); 252 EXPECT_LE(0, autofill_popup_controller_->selected_line()); 253 254 // Remove the first entry. The popup should be redrawn since its size has 255 // changed. 256 EXPECT_CALL(*autofill_popup_controller_, UpdateBoundsAndRedrawPopup()); 257 autofill_popup_controller_->SetSelectedLine(0); 258 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine()); 259 260 // Remove the last entry. The popup should then be hidden since there are 261 // no Autofill entries left. 262 EXPECT_CALL(*autofill_popup_controller_, Hide()); 263 autofill_popup_controller_->SetSelectedLine(0); 264 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine()); 265 } 266 267 TEST_F(AutofillPopupControllerUnitTest, RemoveOnlyLine) { 268 // Set up the popup. 269 std::vector<base::string16> names(1, base::string16()); 270 std::vector<int> autofill_ids; 271 autofill_ids.push_back(1); 272 autofill_popup_controller_->Show(names, names, names, autofill_ids); 273 274 // Generate a popup. 275 autofill::GenerateTestAutofillPopup(external_delegate_.get()); 276 277 // Select the only line. 278 autofill_popup_controller_->SetSelectedLine(0); 279 280 // Remove the only line. There should be no row invalidation and the popup 281 // should then be hidden since there are no Autofill entries left. 282 EXPECT_CALL(*autofill_popup_controller_, Hide()); 283 EXPECT_CALL(*autofill_popup_controller_, InvalidateRow(_)).Times(0); 284 EXPECT_TRUE(autofill_popup_controller_->RemoveSelectedLine()); 285 } 286 287 TEST_F(AutofillPopupControllerUnitTest, SkipSeparator) { 288 // Set up the popup. 289 std::vector<base::string16> names(3, base::string16()); 290 std::vector<int> autofill_ids; 291 autofill_ids.push_back(1); 292 autofill_ids.push_back(POPUP_ITEM_ID_SEPARATOR); 293 autofill_ids.push_back(POPUP_ITEM_ID_AUTOFILL_OPTIONS); 294 autofill_popup_controller_->Show(names, names, names, autofill_ids); 295 296 autofill_popup_controller_->SetSelectedLine(0); 297 298 // Make sure next skips the unselectable separator. 299 autofill_popup_controller_->SelectNextLine(); 300 EXPECT_EQ(2, autofill_popup_controller_->selected_line()); 301 302 // Make sure previous skips the unselectable separator. 303 autofill_popup_controller_->SelectPreviousLine(); 304 EXPECT_EQ(0, autofill_popup_controller_->selected_line()); 305 } 306 307 TEST_F(AutofillPopupControllerUnitTest, RowWidthWithoutText) { 308 std::vector<base::string16> names(4); 309 std::vector<base::string16> subtexts(4); 310 std::vector<base::string16> icons(4); 311 std::vector<int> ids(4); 312 313 // Set up some visible display so the text values are kept. 314 gfx::Display display(0, gfx::Rect(0, 0, 100, 100)); 315 autofill_popup_controller_->set_display(display); 316 317 // Give elements 1 and 3 subtexts and elements 2 and 3 icons, to ensure 318 // all combinations of subtexts and icons. 319 subtexts[1] = ASCIIToUTF16("x"); 320 subtexts[3] = ASCIIToUTF16("x"); 321 icons[2] = ASCIIToUTF16("americanExpressCC"); 322 icons[3] = ASCIIToUTF16("genericCC"); 323 autofill_popup_controller_->Show(names, subtexts, icons, ids); 324 325 int base_size = 326 AutofillPopupView::kEndPadding * 2 + 327 kPopupBorderThickness * 2; 328 int subtext_increase = AutofillPopupView::kNamePadding; 329 330 EXPECT_EQ(base_size, autofill_popup_controller_->RowWidthWithoutText(0)); 331 EXPECT_EQ(base_size + subtext_increase, 332 autofill_popup_controller_->RowWidthWithoutText(1)); 333 EXPECT_EQ(base_size + AutofillPopupView::kIconPadding + 334 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 335 IDR_AUTOFILL_CC_AMEX).Width(), 336 autofill_popup_controller_->RowWidthWithoutText(2)); 337 EXPECT_EQ(base_size + subtext_increase + AutofillPopupView::kIconPadding + 338 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 339 IDR_AUTOFILL_CC_GENERIC).Width(), 340 autofill_popup_controller_->RowWidthWithoutText(3)); 341 } 342 343 TEST_F(AutofillPopupControllerUnitTest, UpdateDataListValues) { 344 std::vector<base::string16> items; 345 items.push_back(base::string16()); 346 std::vector<int> ids; 347 ids.push_back(1); 348 349 autofill_popup_controller_->Show(items, items, items, ids); 350 351 EXPECT_EQ(items, autofill_popup_controller_->names()); 352 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 353 354 // Add one data list entry. 355 std::vector<base::string16> data_list_values; 356 data_list_values.push_back(ASCIIToUTF16("data list value 1")); 357 358 autofill_popup_controller_->UpdateDataListValues(data_list_values, 359 data_list_values); 360 361 // Update the expected values. 362 items.insert(items.begin(), data_list_values[0]); 363 items.insert(items.begin() + 1, base::string16()); 364 ids.insert(ids.begin(), POPUP_ITEM_ID_DATALIST_ENTRY); 365 ids.insert(ids.begin() + 1, POPUP_ITEM_ID_SEPARATOR); 366 367 EXPECT_EQ(items, autofill_popup_controller_->names()); 368 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 369 370 // Add two data list entries (which should replace the current one). 371 data_list_values.push_back(ASCIIToUTF16("data list value 2")); 372 373 autofill_popup_controller_->UpdateDataListValues(data_list_values, 374 data_list_values); 375 376 // Update the expected values. 377 items.insert(items.begin() + 1, data_list_values[1]); 378 ids.insert(ids.begin(), POPUP_ITEM_ID_DATALIST_ENTRY); 379 380 EXPECT_EQ(items, autofill_popup_controller_->names()); 381 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 382 383 // Clear all data list values. 384 data_list_values.clear(); 385 386 autofill_popup_controller_->UpdateDataListValues(data_list_values, 387 data_list_values); 388 389 items.clear(); 390 items.push_back(base::string16()); 391 ids.clear(); 392 ids.push_back(1); 393 394 EXPECT_EQ(items, autofill_popup_controller_->names()); 395 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 396 } 397 398 TEST_F(AutofillPopupControllerUnitTest, PopupsWithOnlyDataLists) { 399 // Create the popup with a single datalist element. 400 std::vector<base::string16> items; 401 items.push_back(base::string16()); 402 std::vector<int> ids; 403 ids.push_back(POPUP_ITEM_ID_DATALIST_ENTRY); 404 405 autofill_popup_controller_->Show(items, items, items, ids); 406 407 EXPECT_EQ(items, autofill_popup_controller_->names()); 408 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 409 410 // Replace the datalist element with a new one. 411 std::vector<base::string16> data_list_values; 412 data_list_values.push_back(ASCIIToUTF16("data list value 1")); 413 414 autofill_popup_controller_->UpdateDataListValues(data_list_values, 415 data_list_values); 416 417 EXPECT_EQ(data_list_values, autofill_popup_controller_->names()); 418 // The id value should stay the same. 419 EXPECT_EQ(ids, autofill_popup_controller_->identifiers()); 420 421 // Clear datalist values and check that the popup becomes hidden. 422 EXPECT_CALL(*autofill_popup_controller_, Hide()); 423 data_list_values.clear(); 424 autofill_popup_controller_->UpdateDataListValues(data_list_values, 425 data_list_values); 426 } 427 428 TEST_F(AutofillPopupControllerUnitTest, GetOrCreate) { 429 ContentAutofillDriver* driver = 430 ContentAutofillDriver::FromWebContents(web_contents()); 431 MockAutofillExternalDelegate delegate(driver->autofill_manager(), driver); 432 433 WeakPtr<AutofillPopupControllerImpl> controller = 434 AutofillPopupControllerImpl::GetOrCreate( 435 WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(), 436 NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION); 437 EXPECT_TRUE(controller.get()); 438 439 controller->Hide(); 440 441 controller = AutofillPopupControllerImpl::GetOrCreate( 442 WeakPtr<AutofillPopupControllerImpl>(), delegate.GetWeakPtr(), 443 NULL, NULL, gfx::Rect(), base::i18n::UNKNOWN_DIRECTION); 444 EXPECT_TRUE(controller.get()); 445 446 WeakPtr<AutofillPopupControllerImpl> controller2 = 447 AutofillPopupControllerImpl::GetOrCreate(controller, 448 delegate.GetWeakPtr(), 449 NULL, 450 NULL, 451 gfx::Rect(), 452 base::i18n::UNKNOWN_DIRECTION); 453 EXPECT_EQ(controller.get(), controller2.get()); 454 controller->Hide(); 455 456 testing::NiceMock<TestAutofillPopupController>* test_controller = 457 new testing::NiceMock<TestAutofillPopupController>(delegate.GetWeakPtr(), 458 gfx::Rect()); 459 EXPECT_CALL(*test_controller, Hide()); 460 461 gfx::RectF bounds(0.f, 0.f, 1.f, 2.f); 462 base::WeakPtr<AutofillPopupControllerImpl> controller3 = 463 AutofillPopupControllerImpl::GetOrCreate( 464 test_controller->GetWeakPtr(), 465 delegate.GetWeakPtr(), 466 NULL, 467 NULL, 468 bounds, 469 base::i18n::UNKNOWN_DIRECTION); 470 EXPECT_EQ( 471 bounds, 472 static_cast<AutofillPopupController*>(controller3.get())-> 473 element_bounds()); 474 controller3->Hide(); 475 476 // Hide the test_controller to delete it. 477 test_controller->DoHide(); 478 } 479 480 TEST_F(AutofillPopupControllerUnitTest, ProperlyResetController) { 481 std::vector<base::string16> names(2); 482 std::vector<int> ids(2); 483 popup_controller()->SetValues(names, names, names, ids); 484 popup_controller()->SetSelectedLine(0); 485 486 // Now show a new popup with the same controller, but with fewer items. 487 WeakPtr<AutofillPopupControllerImpl> controller = 488 AutofillPopupControllerImpl::GetOrCreate( 489 popup_controller()->GetWeakPtr(), 490 delegate()->GetWeakPtr(), 491 NULL, 492 NULL, 493 gfx::Rect(), 494 base::i18n::UNKNOWN_DIRECTION); 495 EXPECT_NE(0, controller->selected_line()); 496 EXPECT_TRUE(controller->names().empty()); 497 } 498 499 #if !defined(OS_ANDROID) 500 TEST_F(AutofillPopupControllerUnitTest, ElideText) { 501 std::vector<base::string16> names; 502 names.push_back(ASCIIToUTF16("Text that will need to be trimmed")); 503 names.push_back(ASCIIToUTF16("Untrimmed")); 504 505 std::vector<base::string16> subtexts; 506 subtexts.push_back(ASCIIToUTF16("Label that will be trimmed")); 507 subtexts.push_back(ASCIIToUTF16("Untrimmed")); 508 509 std::vector<base::string16> icons(2, ASCIIToUTF16("genericCC")); 510 std::vector<int> autofill_ids(2, 0); 511 512 // Show the popup once so we can easily generate the size it needs. 513 autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids); 514 515 // Ensure the popup will be too small to display all of the first row. 516 int popup_max_width = 517 gfx::GetStringWidth( 518 names[0], autofill_popup_controller_->GetNameFontListForRow(0)) + 519 gfx::GetStringWidth( 520 subtexts[0], autofill_popup_controller_->subtext_font_list()) - 25; 521 gfx::Rect popup_bounds = gfx::Rect(0, 0, popup_max_width, 0); 522 autofill_popup_controller_->set_display(gfx::Display(0, popup_bounds)); 523 524 autofill_popup_controller_->Show(names, subtexts, icons, autofill_ids); 525 526 // The first element was long so it should have been trimmed. 527 EXPECT_NE(names[0], autofill_popup_controller_->names()[0]); 528 EXPECT_NE(subtexts[0], autofill_popup_controller_->subtexts()[0]); 529 530 // The second element was shorter so it should be unchanged. 531 EXPECT_EQ(names[1], autofill_popup_controller_->names()[1]); 532 EXPECT_EQ(subtexts[1], autofill_popup_controller_->subtexts()[1]); 533 } 534 #endif 535 536 } // namespace autofill 537