Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/browser/passwords/password_generation_agent.h" | 5 #import "ios/chrome/browser/passwords/password_generation_agent.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <memory> | 8 #include <memory> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/foundation_util.h" | 11 #include "base/mac/foundation_util.h" |
| 12 #import "base/mac/scoped_nsobject.h" | |
| 13 #include "base/mac/scoped_objc_class_swizzler.h" | 12 #include "base/mac/scoped_objc_class_swizzler.h" |
| 14 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/memory/ptr_util.h" | |
| 15 #include "base/strings/string16.h" | 15 #include "base/strings/string16.h" |
| 16 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 18 #include "components/autofill/core/common/form_data.h" | 18 #include "components/autofill/core/common/form_data.h" |
| 19 #include "components/autofill/core/common/form_field_data.h" | 19 #include "components/autofill/core/common/form_field_data.h" |
| 20 #include "components/autofill/core/common/password_form.h" | 20 #include "components/autofill/core/common/password_form.h" |
| 21 #import "components/autofill/ios/browser/js_suggestion_manager.h" | 21 #import "components/autofill/ios/browser/js_suggestion_manager.h" |
| 22 #include "google_apis/gaia/gaia_urls.h" | 22 #include "google_apis/gaia/gaia_urls.h" |
| 23 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h" | 23 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h" |
| 24 #import "ios/chrome/browser/passwords/js_password_manager.h" | 24 #import "ios/chrome/browser/passwords/js_password_manager.h" |
| 25 #import "ios/chrome/browser/passwords/password_generation_offer_view.h" | 25 #import "ios/chrome/browser/passwords/password_generation_offer_view.h" |
| 26 #import "ios/chrome/browser/passwords/passwords_ui_delegate.h" | 26 #import "ios/chrome/browser/passwords/passwords_ui_delegate.h" |
| 27 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" | 27 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" |
| 28 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" | 28 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
| 29 #import "ios/testing/ocmock_complex_type_helper.h" | 29 #import "ios/testing/ocmock_complex_type_helper.h" |
| 30 #include "ios/web/public/test/test_web_state.h" | 30 #include "ios/web/public/test/test_web_state.h" |
| 31 #include "ios/web/public/web_state/url_verification_constants.h" | 31 #include "ios/web/public/web_state/url_verification_constants.h" |
| 32 #import "ios/web/public/test/web_test_with_web_state.h" | 32 #import "ios/web/public/test/web_test_with_web_state.h" |
| 33 #include "testing/gtest_mac.h" | 33 #include "testing/gtest_mac.h" |
| 34 #include "third_party/ocmock/OCMock/OCMock.h" | 34 #include "third_party/ocmock/OCMock/OCMock.h" |
| 35 #include "third_party/ocmock/gtest_support.h" | 35 #include "third_party/ocmock/gtest_support.h" |
| 36 #include "url/gurl.h" | 36 #include "url/gurl.h" |
| 37 | 37 |
| 38 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
| 39 #error "This file requires ARC support." | |
| 40 #endif | |
| 41 | |
| 38 namespace { | 42 namespace { |
| 39 | 43 |
| 40 NSString* const kAccountCreationFormName = @"create-foo-account"; | 44 NSString* const kAccountCreationFormName = @"create-foo-account"; |
| 41 NSString* const kAccountCreationFieldName = @"password"; | 45 NSString* const kAccountCreationFieldName = @"password"; |
| 42 NSString* const kAccountCreationOrigin = @"http://foo.com/login"; | 46 NSString* const kAccountCreationOrigin = @"http://foo.com/login"; |
| 43 NSString* const kEmailFieldName = @"email"; | 47 NSString* const kEmailFieldName = @"email"; |
| 44 | 48 |
| 45 // Static storage to access arguments passed to swizzled method of UIWindow. | 49 // Static storage to access arguments passed to swizzled method of UIWindow. |
| 46 static id g_chrome_execute_command_sender = nil; | 50 static id g_chrome_execute_command_sender = nil; |
| 47 | 51 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 87 // swapped with UIWindow. | 91 // swapped with UIWindow. |
| 88 @interface DonorWindow : NSObject | 92 @interface DonorWindow : NSObject |
| 89 | 93 |
| 90 - (void)chromeExecuteCommand:(id)sender; | 94 - (void)chromeExecuteCommand:(id)sender; |
| 91 | 95 |
| 92 @end | 96 @end |
| 93 | 97 |
| 94 @implementation DonorWindow | 98 @implementation DonorWindow |
| 95 | 99 |
| 96 - (void)chromeExecuteCommand:(id)sender { | 100 - (void)chromeExecuteCommand:(id)sender { |
| 97 g_chrome_execute_command_sender = [sender retain]; | 101 g_chrome_execute_command_sender = sender; |
| 98 } | 102 } |
| 99 | 103 |
| 100 @end | 104 @end |
| 101 | 105 |
| 102 namespace { | 106 namespace { |
| 103 | 107 |
| 104 // A helper to swizzle chromeExecuteCommand method on UIWindow. | 108 // A helper to swizzle chromeExecuteCommand method on UIWindow. |
| 105 class ScopedWindowSwizzler { | 109 class ScopedWindowSwizzler { |
| 106 public: | 110 public: |
| 107 ScopedWindowSwizzler() | 111 ScopedWindowSwizzler() |
| 108 : class_swizzler_([UIWindow class], | 112 : class_swizzler_([UIWindow class], |
| 109 [DonorWindow class], | 113 [DonorWindow class], |
| 110 @selector(chromeExecuteCommand:)) { | 114 @selector(chromeExecuteCommand:)) { |
| 111 DCHECK(!g_chrome_execute_command_sender); | 115 DCHECK(!g_chrome_execute_command_sender); |
| 112 } | 116 } |
| 113 | 117 |
| 114 ~ScopedWindowSwizzler() { | 118 ~ScopedWindowSwizzler() { |
| 115 [g_chrome_execute_command_sender release]; | |
| 116 g_chrome_execute_command_sender = nil; | 119 g_chrome_execute_command_sender = nil; |
| 117 } | 120 } |
| 118 | 121 |
| 119 private: | 122 private: |
| 120 base::mac::ScopedObjCClassSwizzler class_swizzler_; | 123 base::mac::ScopedObjCClassSwizzler class_swizzler_; |
| 121 | 124 |
| 122 DISALLOW_COPY_AND_ASSIGN(ScopedWindowSwizzler); | 125 DISALLOW_COPY_AND_ASSIGN(ScopedWindowSwizzler); |
| 123 }; | 126 }; |
| 124 | 127 |
| 125 // Returns a form that should be marked as an account creation form by local | 128 // Returns a form that should be marked as an account creation form by local |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 &IsPasswordField), | 211 &IsPasswordField), |
| 209 form.form_data.fields.end()); | 212 form.form_data.fields.end()); |
| 210 return form; | 213 return form; |
| 211 } | 214 } |
| 212 | 215 |
| 213 // Test fixture for testing PasswordGenerationAgent. | 216 // Test fixture for testing PasswordGenerationAgent. |
| 214 class PasswordGenerationAgentTest : public web::WebTestWithWebState { | 217 class PasswordGenerationAgentTest : public web::WebTestWithWebState { |
| 215 public: | 218 public: |
| 216 void SetUp() override { | 219 void SetUp() override { |
| 217 web::WebTestWithWebState::SetUp(); | 220 web::WebTestWithWebState::SetUp(); |
| 218 mock_js_suggestion_manager_.reset( | 221 mock_js_suggestion_manager_ = |
| 219 [[OCMockObject niceMockForClass:[JsSuggestionManager class]] retain]); | 222 [OCMockObject niceMockForClass:[JsSuggestionManager class]]; |
| 220 mock_js_password_manager_.reset( | 223 mock_js_password_manager_ = |
| 221 [[OCMockObject niceMockForClass:[JsPasswordManager class]] retain]); | 224 [OCMockObject niceMockForClass:[JsPasswordManager class]]; |
| 222 mock_ui_delegate_.reset([[MockPasswordsUiDelegate alloc] init]); | 225 mock_ui_delegate_ = [[MockPasswordsUiDelegate alloc] init]; |
| 223 test_web_state_.reset(new web::TestWebState); | 226 test_web_state_ = base::MakeUnique<web::TestWebState>(); |
| 224 agent_.reset([[PasswordGenerationAgent alloc] | 227 agent_ = [[PasswordGenerationAgent alloc] |
| 225 initWithWebState:test_web_state_.get() | 228 initWithWebState:test_web_state_.get() |
| 226 passwordManager:nullptr | 229 passwordManager:nullptr |
| 227 passwordManagerDriver:nullptr | 230 passwordManagerDriver:nullptr |
| 228 JSPasswordManager:mock_js_password_manager_ | 231 JSPasswordManager:mock_js_password_manager_ |
| 229 JSSuggestionManager:mock_js_suggestion_manager_ | 232 JSSuggestionManager:mock_js_suggestion_manager_ |
| 230 passwordsUiDelegate:mock_ui_delegate_]); | 233 passwordsUiDelegate:mock_ui_delegate_]; |
| 231 @autoreleasepool { | 234 @autoreleasepool { |
| 232 accessory_view_controller_.reset([[FormInputAccessoryViewController alloc] | 235 accessory_view_controller_ = [[FormInputAccessoryViewController alloc] |
| 233 initWithWebState:test_web_state_.get() | 236 initWithWebState:test_web_state_.get() |
| 234 providers:@[ agent_ ]]); | 237 providers:@[ agent_ ]]; |
| 235 } | 238 } |
| 236 } | 239 } |
| 237 | 240 |
| 238 // Sends form data, autofill data, and password manager data to the | 241 // Sends form data, autofill data, and password manager data to the |
| 239 // generation agent so that it can find an account creation form and password | 242 // generation agent so that it can find an account creation form and password |
| 240 // field. | 243 // field. |
| 241 void LoadAccountCreationForm() { | 244 void LoadAccountCreationForm() { |
| 242 autofill::PasswordForm password_form(GetAccountCreationForm()); | 245 autofill::PasswordForm password_form(GetAccountCreationForm()); |
| 243 [agent() allowPasswordGenerationForForm:password_form]; | 246 [agent() allowPasswordGenerationForForm:password_form]; |
| 244 std::vector<autofill::PasswordForm> password_forms; | 247 std::vector<autofill::PasswordForm> password_forms; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 280 // Returns a mock of JsSuggestionManager. | 283 // Returns a mock of JsSuggestionManager. |
| 281 id mock_js_suggestion_manager() { return mock_js_suggestion_manager_; } | 284 id mock_js_suggestion_manager() { return mock_js_suggestion_manager_; } |
| 282 | 285 |
| 283 // Returns a mock of JsPasswordManager. | 286 // Returns a mock of JsPasswordManager. |
| 284 id mock_js_password_manager() { return mock_js_password_manager_; } | 287 id mock_js_password_manager() { return mock_js_password_manager_; } |
| 285 | 288 |
| 286 MockPasswordsUiDelegate* mock_ui_delegate() { return mock_ui_delegate_; } | 289 MockPasswordsUiDelegate* mock_ui_delegate() { return mock_ui_delegate_; } |
| 287 | 290 |
| 288 protected: | 291 protected: |
| 289 // Returns the current generation agent. | 292 // Returns the current generation agent. |
| 290 PasswordGenerationAgent* agent() { return agent_.get(); } | 293 PasswordGenerationAgent* agent() { return agent_; } |
| 291 | 294 |
| 292 // Returns the current accessory view controller. | 295 // Returns the current accessory view controller. |
| 293 FormInputAccessoryViewController* accessory_controller() { | 296 FormInputAccessoryViewController* accessory_controller() { |
| 294 return accessory_view_controller_.get(); | 297 return accessory_view_controller_; |
| 295 } | 298 } |
| 296 | 299 |
| 297 private: | 300 private: |
| 298 // Test WebState. | 301 // Test WebState. |
| 299 std::unique_ptr<web::TestWebState> test_web_state_; | 302 std::unique_ptr<web::TestWebState> test_web_state_; |
| 300 | 303 |
| 301 // Mock for JsSuggestionManager; | 304 // Mock for JsSuggestionManager; |
| 302 base::scoped_nsobject<id> mock_js_suggestion_manager_; | 305 id mock_js_suggestion_manager_; |
| 303 | 306 |
| 304 // Mock for JsPasswordManager. | 307 // Mock for JsPasswordManager. |
| 305 base::scoped_nsobject<id> mock_js_password_manager_; | 308 id mock_js_password_manager_; |
| 306 | 309 |
| 307 // Mock for the UI delegate. | 310 // Mock for the UI delegate. |
| 308 base::scoped_nsobject<MockPasswordsUiDelegate> mock_ui_delegate_; | 311 MockPasswordsUiDelegate* mock_ui_delegate_; |
| 309 | 312 |
| 310 // Controller that shows custom input accessory views. | 313 // Controller that shows custom input accessory views. |
| 311 base::scoped_nsobject<FormInputAccessoryViewController> | 314 FormInputAccessoryViewController* accessory_view_controller_; |
| 312 accessory_view_controller_; | |
| 313 | 315 |
| 314 // The current generation agent. | 316 // The current generation agent. |
| 315 base::scoped_nsobject<PasswordGenerationAgent> agent_; | 317 PasswordGenerationAgent* agent_; |
| 316 }; | 318 }; |
| 317 | 319 |
| 318 // Tests that local heuristics skip forms with GAIA realm. | 320 // Tests that local heuristics skip forms with GAIA realm. |
| 319 TEST_F(PasswordGenerationAgentTest, | 321 TEST_F(PasswordGenerationAgentTest, |
| 320 OnParsedForms_ShouldIgnoreFormsWithGaiaRealm) { | 322 OnParsedForms_ShouldIgnoreFormsWithGaiaRealm) { |
| 321 // Send only a form with GAIA origin to the agent. | 323 // Send only a form with GAIA origin to the agent. |
| 322 std::vector<autofill::PasswordForm> forms; | 324 std::vector<autofill::PasswordForm> forms; |
| 323 forms.push_back(GetGAIAForm()); | 325 forms.push_back(GetGAIAForm()); |
| 324 [agent() processParsedPasswordForms:forms]; | 326 [agent() processParsedPasswordForms:forms]; |
| 325 | 327 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 475 // password. | 477 // password. |
| 476 TEST_F(PasswordGenerationAgentTest, | 478 TEST_F(PasswordGenerationAgentTest, |
| 477 ShouldFillPasswordFieldAndDismissAlertWhenUserAcceptsGeneratedPassword) { | 479 ShouldFillPasswordFieldAndDismissAlertWhenUserAcceptsGeneratedPassword) { |
| 478 LoadAccountCreationForm(); | 480 LoadAccountCreationForm(); |
| 479 // Focus the password field to start generation. | 481 // Focus the password field to start generation. |
| 480 SimulateFormActivity(kAccountCreationFormName, kAccountCreationFieldName, | 482 SimulateFormActivity(kAccountCreationFormName, kAccountCreationFieldName, |
| 481 @"focus"); | 483 @"focus"); |
| 482 NSString* password = @"abc"; | 484 NSString* password = @"abc"; |
| 483 | 485 |
| 484 [[[mock_js_password_manager() stub] andDo:^(NSInvocation* invocation) { | 486 [[[mock_js_password_manager() stub] andDo:^(NSInvocation* invocation) { |
| 485 void (^completion_handler)(BOOL); | 487 __unsafe_unretained void (^completion_handler)(BOOL); |
|
Eugene But (OOO till 7-30)
2016/12/07 18:21:35
ditto
stkhapugin
2016/12/08 10:15:47
See above.
| |
| 486 [invocation getArgument:&completion_handler atIndex:4]; | 488 [invocation getArgument:&completion_handler atIndex:4]; |
| 487 completion_handler(YES); | 489 completion_handler(YES); |
| 488 }] fillPasswordForm:kAccountCreationFormName | 490 }] fillPasswordForm:kAccountCreationFormName |
| 489 withGeneratedPassword:password | 491 withGeneratedPassword:password |
| 490 completionHandler:[OCMArg any]]; | 492 completionHandler:[OCMArg any]]; |
| 491 | 493 |
| 492 [agent() generatePassword]; | 494 [agent() generatePassword]; |
| 493 EXPECT_EQ(YES, mock_ui_delegate().UIShown); | 495 EXPECT_EQ(YES, mock_ui_delegate().UIShown); |
| 494 | 496 |
| 495 [agent() acceptPasswordGeneration:nil]; | 497 [agent() acceptPasswordGeneration:nil]; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 512 [agent() showSavedPasswords:nil]; | 514 [agent() showSavedPasswords:nil]; |
| 513 EXPECT_EQ(NO, mock_ui_delegate().UIShown); | 515 EXPECT_EQ(NO, mock_ui_delegate().UIShown); |
| 514 | 516 |
| 515 GenericChromeCommand* command = base::mac::ObjCCast<GenericChromeCommand>( | 517 GenericChromeCommand* command = base::mac::ObjCCast<GenericChromeCommand>( |
| 516 g_chrome_execute_command_sender); | 518 g_chrome_execute_command_sender); |
| 517 EXPECT_TRUE(command); | 519 EXPECT_TRUE(command); |
| 518 EXPECT_EQ(IDC_SHOW_SAVE_PASSWORDS_SETTINGS, command.tag); | 520 EXPECT_EQ(IDC_SHOW_SAVE_PASSWORDS_SETTINGS, command.tag); |
| 519 } | 521 } |
| 520 | 522 |
| 521 } // namespace | 523 } // namespace |
| OLD | NEW |