1 // Copyright 2013 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 <vector> 6 7 #include "base/compiler_specific.h" 8 #include "base/strings/string16.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 11 #include "chrome/test/base/testing_profile.h" 12 #include "components/autofill/core/browser/autofill_manager.h" 13 #include "components/autofill/core/browser/test_autofill_driver.h" 14 #include "components/autofill/core/browser/test_autofill_external_delegate.h" 15 #include "components/autofill/core/browser/test_autofill_manager_delegate.h" 16 #include "components/autofill/core/common/form_data.h" 17 #include "components/autofill/core/common/form_field_data.h" 18 #include "components/autofill/core/common/password_form_fill_data.h" 19 #include "testing/gmock/include/gmock/gmock.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "third_party/WebKit/public/web/WebAutofillClient.h" 22 #include "ui/gfx/rect.h" 23 24 using testing::_; 25 using WebKit::WebAutofillClient; 26 27 namespace autofill { 28 29 namespace { 30 31 // A constant value to use as the Autofill query ID. 32 const int kQueryId = 5; 33 34 // A constant value to use as an Autofill profile ID. 35 const int kAutofillProfileId = 1; 36 37 class MockAutofillDriver : public TestAutofillDriver { 38 public: 39 explicit MockAutofillDriver(content::WebContents* web_contents) 40 : TestAutofillDriver(web_contents) {} 41 42 // Mock methods to enable testability. 43 MOCK_METHOD1(SetRendererActionOnFormDataReception, 44 void(RendererFormDataAction action)); 45 MOCK_METHOD0(RendererShouldClearFilledForm, void()); 46 MOCK_METHOD0(RendererShouldClearPreviewedForm, void()); 47 48 private: 49 DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver); 50 }; 51 52 class MockAutofillManagerDelegate 53 : public autofill::TestAutofillManagerDelegate { 54 public: 55 MockAutofillManagerDelegate() {} 56 57 MOCK_METHOD7(ShowAutofillPopup, 58 void(const gfx::RectF& element_bounds, 59 base::i18n::TextDirection text_direction, 60 const std::vector<base::string16>& values, 61 const std::vector<base::string16>& labels, 62 const std::vector<base::string16>& icons, 63 const std::vector<int>& identifiers, 64 base::WeakPtr<AutofillPopupDelegate> delegate)); 65 66 MOCK_METHOD2(UpdateAutofillPopupDataListValues, 67 void(const std::vector<base::string16>& values, 68 const std::vector<base::string16>& lables)); 69 70 MOCK_METHOD0(HideAutofillPopup, void()); 71 72 private: 73 DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate); 74 }; 75 76 class MockAutofillManager : public AutofillManager { 77 public: 78 MockAutofillManager(AutofillDriver* driver, 79 MockAutofillManagerDelegate* delegate) 80 // Force to use the constructor designated for unit test, but we don't 81 // really need personal_data in this test so we pass a NULL pointer. 82 : AutofillManager(driver, delegate, NULL) { 83 } 84 virtual ~MockAutofillManager() {} 85 86 MOCK_METHOD4(OnFillAutofillFormData, 87 void(int query_id, 88 const FormData& form, 89 const FormFieldData& field, 90 int unique_id)); 91 92 private: 93 DISALLOW_COPY_AND_ASSIGN(MockAutofillManager); 94 }; 95 96 } // namespace 97 98 class AutofillExternalDelegateUnitTest 99 : public ChromeRenderViewHostTestHarness { 100 protected: 101 virtual void SetUp() OVERRIDE { 102 ChromeRenderViewHostTestHarness::SetUp(); 103 autofill_driver_.reset(new MockAutofillDriver(web_contents())); 104 autofill_manager_.reset( 105 new MockAutofillManager(autofill_driver_.get(), 106 &manager_delegate_)); 107 external_delegate_.reset( 108 new AutofillExternalDelegate( 109 web_contents(), 110 autofill_manager_.get(), autofill_driver_.get())); 111 } 112 113 virtual void TearDown() OVERRIDE { 114 // Order of destruction is important as AutofillManager relies on 115 // PersonalDataManager to be around when it gets destroyed. Also, a real 116 // AutofillManager is tied to the lifetime of the WebContents, so it must 117 // be destroyed at the destruction of the WebContents. 118 autofill_manager_.reset(); 119 external_delegate_.reset(); 120 autofill_driver_.reset(); 121 ChromeRenderViewHostTestHarness::TearDown(); 122 } 123 124 // Issue an OnQuery call with the given |query_id|. 125 void IssueOnQuery(int query_id) { 126 const FormData form; 127 FormFieldData field; 128 field.is_focusable = true; 129 field.should_autocomplete = true; 130 const gfx::RectF element_bounds; 131 132 external_delegate_->OnQuery(query_id, form, field, element_bounds, true); 133 } 134 135 MockAutofillManagerDelegate manager_delegate_; 136 scoped_ptr<MockAutofillDriver> autofill_driver_; 137 scoped_ptr<MockAutofillManager> autofill_manager_; 138 scoped_ptr<AutofillExternalDelegate> external_delegate_; 139 }; 140 141 // Test that our external delegate called the virtual methods at the right time. 142 TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) { 143 IssueOnQuery(kQueryId); 144 145 // The enums must be cast to ints to prevent compile errors on linux_rel. 146 EXPECT_CALL(manager_delegate_, 147 ShowAutofillPopup( 148 _, _, _, _, _, 149 testing::ElementsAre( 150 kAutofillProfileId, 151 static_cast<int>(WebAutofillClient::MenuItemIDSeparator), 152 static_cast<int>( 153 WebAutofillClient::MenuItemIDAutofillOptions)), 154 _)); 155 156 // This should call ShowAutofillPopup. 157 std::vector<base::string16> autofill_item; 158 autofill_item.push_back(base::string16()); 159 std::vector<int> autofill_ids; 160 autofill_ids.push_back(kAutofillProfileId); 161 external_delegate_->OnSuggestionsReturned(kQueryId, 162 autofill_item, 163 autofill_item, 164 autofill_item, 165 autofill_ids); 166 167 // Called by DidAutofillSuggestions, add expectation to remove warning. 168 EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)); 169 170 EXPECT_CALL(*autofill_driver_, SetRendererActionOnFormDataReception( 171 AutofillDriver::FORM_DATA_ACTION_FILL)); 172 173 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 174 175 // This should trigger a call to hide the popup since we've selected an 176 // option. 177 external_delegate_->DidAcceptSuggestion(autofill_item[0], autofill_ids[0]); 178 } 179 180 // Test that data list elements for a node will appear in the Autofill popup. 181 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) { 182 IssueOnQuery(kQueryId); 183 184 std::vector<base::string16> data_list_items; 185 data_list_items.push_back(base::string16()); 186 187 external_delegate_->SetCurrentDataListValues(data_list_items, 188 data_list_items); 189 190 // The enums must be cast to ints to prevent compile errors on linux_rel. 191 EXPECT_CALL(manager_delegate_, 192 ShowAutofillPopup( 193 _, _, _, _, _, 194 testing::ElementsAre( 195 static_cast<int>( 196 WebAutofillClient::MenuItemIDDataListEntry), 197 static_cast<int>(WebAutofillClient::MenuItemIDSeparator), 198 kAutofillProfileId, 199 static_cast<int>(WebAutofillClient::MenuItemIDSeparator), 200 static_cast<int>( 201 WebAutofillClient::MenuItemIDAutofillOptions)), 202 _)); 203 204 // This should call ShowAutofillPopup. 205 std::vector<base::string16> autofill_item; 206 autofill_item.push_back(base::string16()); 207 std::vector<int> autofill_ids; 208 autofill_ids.push_back(kAutofillProfileId); 209 external_delegate_->OnSuggestionsReturned(kQueryId, 210 autofill_item, 211 autofill_item, 212 autofill_item, 213 autofill_ids); 214 215 // Try calling OnSuggestionsReturned with no Autofill values and ensure 216 // the datalist items are still shown. 217 // The enum must be cast to an int to prevent compile errors on linux_rel. 218 EXPECT_CALL(manager_delegate_, 219 ShowAutofillPopup( 220 _, _, _, _, _, 221 testing::ElementsAre( 222 static_cast<int>( 223 WebAutofillClient::MenuItemIDDataListEntry)), 224 _)); 225 226 autofill_item = std::vector<base::string16>(); 227 autofill_ids = std::vector<int>(); 228 external_delegate_->OnSuggestionsReturned(kQueryId, 229 autofill_item, 230 autofill_item, 231 autofill_item, 232 autofill_ids); 233 } 234 235 // Test that datalist values can get updated while a popup is showing. 236 TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) { 237 IssueOnQuery(kQueryId); 238 239 EXPECT_CALL(manager_delegate_, 240 ShowAutofillPopup(_, _, _, _, _, _, _)).Times(0); 241 242 // Make sure just setting the data list values doesn't cause the popup to 243 // appear. 244 std::vector<base::string16> data_list_items; 245 data_list_items.push_back(base::string16()); 246 247 external_delegate_->SetCurrentDataListValues(data_list_items, 248 data_list_items); 249 250 // The enums must be cast to ints to prevent compile errors on linux_rel. 251 EXPECT_CALL(manager_delegate_, 252 ShowAutofillPopup( 253 _, _, _, _, _, 254 testing::ElementsAre( 255 static_cast<int>( 256 WebAutofillClient::MenuItemIDDataListEntry), 257 static_cast<int>(WebAutofillClient::MenuItemIDSeparator), 258 kAutofillProfileId, 259 static_cast<int>(WebAutofillClient::MenuItemIDSeparator), 260 static_cast<int>( 261 WebAutofillClient::MenuItemIDAutofillOptions)), 262 _)); 263 264 // Ensure the popup is displayed. 265 std::vector<base::string16> autofill_item; 266 autofill_item.push_back(base::string16()); 267 std::vector<int> autofill_ids; 268 autofill_ids.push_back(kAutofillProfileId); 269 external_delegate_->OnSuggestionsReturned(kQueryId, 270 autofill_item, 271 autofill_item, 272 autofill_item, 273 autofill_ids); 274 275 // This would normally get called from ShowAutofillPopup, but it is mocked 276 // we need to call OnPopupShown ourselves. 277 external_delegate_->OnPopupShown(NULL); 278 279 // Update the current data list and ensure the popup is updated. 280 data_list_items.push_back(base::string16()); 281 282 // The enums must be cast to ints to prevent compile errors on linux_rel. 283 EXPECT_CALL(manager_delegate_, 284 UpdateAutofillPopupDataListValues(data_list_items, 285 data_list_items)); 286 287 external_delegate_->SetCurrentDataListValues(data_list_items, 288 data_list_items); 289 } 290 291 // Test that the Autofill popup is able to display warnings explaining why 292 // Autofill is disabled for a website. 293 // Regression test for http://crbug.com/247880 294 TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) { 295 IssueOnQuery(kQueryId); 296 297 // The enums must be cast to ints to prevent compile errors on linux_rel. 298 EXPECT_CALL(manager_delegate_, 299 ShowAutofillPopup( 300 _, _, _, _, _, 301 testing::ElementsAre( 302 static_cast<int>( 303 WebAutofillClient::MenuItemIDWarningMessage)), 304 _)); 305 306 // This should call ShowAutofillPopup. 307 std::vector<base::string16> autofill_item; 308 autofill_item.push_back(base::string16()); 309 std::vector<int> autofill_ids; 310 autofill_ids.push_back(WebAutofillClient::MenuItemIDWarningMessage); 311 external_delegate_->OnSuggestionsReturned(kQueryId, 312 autofill_item, 313 autofill_item, 314 autofill_item, 315 autofill_ids); 316 } 317 318 // Test that the Autofill popup doesn't display a warning explaining why 319 // Autofill is disabled for a website when there are no Autofill suggestions. 320 // Regression test for http://crbug.com/105636 321 TEST_F(AutofillExternalDelegateUnitTest, NoAutofillWarningsWithoutSuggestions) { 322 const FormData form; 323 FormFieldData field; 324 field.is_focusable = true; 325 field.should_autocomplete = false; 326 const gfx::RectF element_bounds; 327 328 external_delegate_->OnQuery(kQueryId, form, field, element_bounds, true); 329 330 EXPECT_CALL(manager_delegate_, 331 ShowAutofillPopup(_, _, _, _, _, _, _)).Times(0); 332 EXPECT_CALL(manager_delegate_, HideAutofillPopup()).Times(1); 333 334 // This should not call ShowAutofillPopup. 335 std::vector<base::string16> autofill_item; 336 autofill_item.push_back(base::string16()); 337 std::vector<int> autofill_ids; 338 autofill_ids.push_back(WebAutofillClient::MenuItemIDAutocompleteEntry); 339 external_delegate_->OnSuggestionsReturned(kQueryId, 340 autofill_item, 341 autofill_item, 342 autofill_item, 343 autofill_ids); 344 } 345 346 // Test that the Autofill delegate doesn't try and fill a form with a 347 // negative unique id. 348 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateInvalidUniqueId) { 349 // Ensure it doesn't try to preview the negative id. 350 EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0); 351 EXPECT_CALL(*autofill_driver_, 352 SetRendererActionOnFormDataReception(_)).Times(0); 353 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1); 354 external_delegate_->DidSelectSuggestion(-1); 355 356 // Ensure it doesn't try to fill the form in with the negative id. 357 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 358 EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0); 359 EXPECT_CALL(*autofill_driver_, 360 SetRendererActionOnFormDataReception(_)).Times(0); 361 external_delegate_->DidAcceptSuggestion(base::string16(), -1); 362 } 363 364 // Test that the ClearPreview call is only sent if the form was being previewed 365 // (i.e. it isn't autofilling a password). 366 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearPreviewedForm) { 367 // Called by DidSelectSuggestion, add expectation to remove warning. 368 EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)); 369 370 // Ensure selecting a new password entries or Autofill entries will 371 // cause any previews to get cleared. 372 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1); 373 external_delegate_->DidSelectSuggestion( 374 WebAutofillClient::MenuItemIDPasswordEntry); 375 376 EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1); 377 EXPECT_CALL(*autofill_driver_, SetRendererActionOnFormDataReception( 378 AutofillDriver::FORM_DATA_ACTION_PREVIEW)); 379 external_delegate_->DidSelectSuggestion(1); 380 } 381 382 // Test that the popup is hidden once we are done editing the autofill field. 383 TEST_F(AutofillExternalDelegateUnitTest, 384 ExternalDelegateHidePopupAfterEditing) { 385 EXPECT_CALL(manager_delegate_, ShowAutofillPopup(_, _, _, _, _, _, _)); 386 autofill::GenerateTestAutofillPopup(external_delegate_.get()); 387 388 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 389 external_delegate_->DidEndTextFieldEditing(); 390 } 391 392 // Test that the popup is marked as visible after recieving password 393 // suggestions. 394 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegatePasswordSuggestions) { 395 static const base::string16 kUsername = ASCIIToUTF16("username"); 396 static const base::string16 kSignonRealm = ASCIIToUTF16("http://foo.com/"); 397 std::vector<base::string16> suggestions; 398 suggestions.push_back(kUsername); 399 std::vector<base::string16> realms; 400 realms.push_back(kSignonRealm); 401 402 FormFieldData field; 403 field.is_focusable = true; 404 field.should_autocomplete = true; 405 const gfx::RectF element_bounds; 406 407 FormFieldData username_field_data; 408 username_field_data.value = kUsername; 409 PasswordFormFillData password_form_fill_data; 410 password_form_fill_data.basic_data.fields.push_back(username_field_data); 411 external_delegate_->AddPasswordFormMapping(field, password_form_fill_data); 412 413 // The enums must be cast to ints to prevent compile errors on linux_rel. 414 EXPECT_CALL(manager_delegate_, 415 ShowAutofillPopup( 416 _, _, _, _, _, 417 testing::ElementsAre( 418 static_cast<int>( 419 WebAutofillClient::MenuItemIDPasswordEntry)), 420 _)); 421 422 external_delegate_->OnShowPasswordSuggestions(suggestions, 423 realms, 424 field, 425 element_bounds); 426 427 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 428 429 // This should trigger a call to hide the popup since 430 // we've selected an option. 431 external_delegate_->DidAcceptSuggestion( 432 suggestions[0], 433 WebAutofillClient::MenuItemIDPasswordEntry); 434 } 435 436 // Test that the driver is directed to clear the form after being notified that 437 // the user accepted the suggestion to clear the form. 438 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearForm) { 439 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 440 EXPECT_CALL(*autofill_driver_, RendererShouldClearFilledForm()); 441 442 external_delegate_->DidAcceptSuggestion( 443 base::string16(), 444 WebAutofillClient::MenuItemIDClearForm); 445 } 446 447 TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateHideWarning) { 448 // Set up a field that shouldn't get autocompleted or display warnings. 449 const FormData form; 450 FormFieldData field; 451 field.is_focusable = true; 452 field.should_autocomplete = false; 453 const gfx::RectF element_bounds; 454 455 external_delegate_->OnQuery(kQueryId, form, field, element_bounds, false); 456 457 std::vector<base::string16> autofill_items; 458 autofill_items.push_back(base::string16()); 459 std::vector<int> autofill_ids; 460 autofill_ids.push_back(WebAutofillClient::MenuItemIDAutocompleteEntry); 461 462 // Ensure the popup tries to hide itself, since it is not allowed to show 463 // anything. 464 EXPECT_CALL(manager_delegate_, HideAutofillPopup()); 465 466 external_delegate_->OnSuggestionsReturned(kQueryId, 467 autofill_items, 468 autofill_items, 469 autofill_items, 470 autofill_ids); 471 } 472 473 } // namespace autofill 474