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 |