Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: ios/chrome/browser/ui/authentication/signin_interaction_controller.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "ios/chrome/browser/ui/authentication/signin_interaction_controller.h"
6
7 #include "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/scoped_block.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "components/prefs/pref_service.h"
13 #include "components/signin/core/browser/signin_manager.h"
14 #include "components/signin/core/common/signin_pref_names.h"
15 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
16 #include "ios/chrome/browser/signin/authentication_service.h"
17 #include "ios/chrome/browser/signin/authentication_service_factory.h"
18 #include "ios/chrome/browser/signin/signin_manager_factory.h"
19 #import "ios/chrome/browser/signin/signin_util.h"
20 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
21 #import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
22 #import "ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h"
23 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
24 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
25 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
26 #import "ios/public/provider/chrome/browser/signin/chrome_identity_interaction_m anager.h"
27 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
28
29 using signin_ui::CompletionCallback;
30
31 @interface SigninInteractionController ()<
32 ChromeIdentityInteractionManagerDelegate,
33 ChromeSigninViewControllerDelegate> {
34 ios::ChromeBrowserState* browserState_;
35 signin_metrics::AccessPoint signInAccessPoint_;
36 base::scoped_nsobject<UIViewController> presentingViewController_;
37 BOOL isPresentedOnSettings_;
38 BOOL isCancelling_;
39 BOOL isDismissing_;
40 BOOL interactionManagerDismissalIgnored_;
41 base::scoped_nsobject<AlertCoordinator> alertCoordinator_;
42 base::mac::ScopedBlock<CompletionCallback> completionCallback_;
43 base::scoped_nsobject<ChromeSigninViewController> signinViewController_;
44 base::scoped_nsobject<ChromeIdentityInteractionManager>
45 identityInteractionManager_;
46 base::scoped_nsobject<ChromeIdentity> signInIdentity_;
47 }
48 @end
49
50 @implementation SigninInteractionController
51
52 - (id)init {
53 NOTREACHED();
54 return nil;
55 }
56
57 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
58 presentingViewController:(UIViewController*)presentingViewController
59 isPresentedOnSettings:(BOOL)isPresentedOnSettings
60 signInAccessPoint:(signin_metrics::AccessPoint)accessPoint {
61 self = [super init];
62 if (self) {
63 DCHECK(browserState);
64 DCHECK(presentingViewController);
65 browserState_ = browserState;
66 presentingViewController_.reset([presentingViewController retain]);
67 isPresentedOnSettings_ = isPresentedOnSettings;
68 signInAccessPoint_ = accessPoint;
69 }
70 return self;
71 }
72
73 - (void)cancel {
74 // Cancelling and dismissing the |identityInteractionManager_| may call the
75 // |completionCallback_| which could lead to |self| being released before the
76 // end of this method. |self| is retained here to prevent this from happening.
77 base::scoped_nsobject<SigninInteractionController> strongSelf([self retain]);
78 isCancelling_ = YES;
79 [alertCoordinator_ executeCancelHandler];
80 [alertCoordinator_ stop];
81 [identityInteractionManager_ cancelAndDismissAnimated:NO];
82 [signinViewController_ cancel];
83 isCancelling_ = NO;
84 }
85
86 - (void)cancelAndDismiss {
87 isDismissing_ = YES;
88 [self cancel];
89 isDismissing_ = NO;
90 }
91
92 - (void)signInWithCompletion:(CompletionCallback)completion
93 viewController:(UIViewController*)viewController {
94 signin_metrics::LogSigninAccessPointStarted(signInAccessPoint_);
95 completionCallback_.reset(completion, base::scoped_policy::RETAIN);
96 if (ios::GetChromeBrowserProvider()
97 ->GetChromeIdentityService()
98 ->HasIdentities()) {
99 DCHECK(!signinViewController_);
100 [self showSigninViewControllerWithIdentity:nil];
101 } else {
102 identityInteractionManager_ =
103 ios::GetChromeBrowserProvider()
104 ->GetChromeIdentityService()
105 ->NewChromeIdentityInteractionManager(browserState_, self);
106 if (!identityInteractionManager_) {
107 // Abort sign-in if the ChromeIdentityInteractionManager returned is
108 // nil (this can happen when the iOS internal provider is not used).
109 [self runCompletionCallbackWithSuccess:NO executeCommand:nil];
110 return;
111 }
112
113 base::WeakNSObject<SigninInteractionController> weakSelf(self);
114 [identityInteractionManager_
115 addAccountWithCompletion:^(ChromeIdentity* identity, NSError* error) {
116 [weakSelf handleIdentityAdded:identity
117 error:error
118 shouldSignIn:YES
119 viewController:viewController];
120 }];
121 }
122 }
123
124 - (void)reAuthenticateWithCompletion:(CompletionCallback)completion
125 viewController:(UIViewController*)viewController {
126 signin_metrics::LogSigninAccessPointStarted(signInAccessPoint_);
127 completionCallback_.reset(completion, base::scoped_policy::RETAIN);
128 AccountInfo accountInfo =
129 ios::SigninManagerFactory::GetForBrowserState(browserState_)
130 ->GetAuthenticatedAccountInfo();
131 std::string emailToReauthenticate = accountInfo.email;
132 std::string idToReauthenticate = accountInfo.gaia;
133 if (emailToReauthenticate.empty() || idToReauthenticate.empty()) {
134 // This corresponds to a re-authenticate request after the user was signed
135 // out. This corresponds to the case where the identity was removed as a
136 // result of the permissions being removed on the server or the identity
137 // being removed from another app.
138 //
139 // Simply use the the last signed-in user email in this case and go though
140 // the entire sign-in flow as sync needs to be configured.
141 emailToReauthenticate = browserState_->GetPrefs()->GetString(
142 prefs::kGoogleServicesLastUsername);
143 idToReauthenticate = browserState_->GetPrefs()->GetString(
144 prefs::kGoogleServicesLastAccountId);
145 }
146 DCHECK(!emailToReauthenticate.empty());
147 DCHECK(!idToReauthenticate.empty());
148 identityInteractionManager_ =
149 ios::GetChromeBrowserProvider()
150 ->GetChromeIdentityService()
151 ->NewChromeIdentityInteractionManager(browserState_, self);
152 base::WeakNSObject<SigninInteractionController> weakSelf(self);
153 [identityInteractionManager_
154 reauthenticateUserWithID:base::SysUTF8ToNSString(idToReauthenticate)
155 email:base::SysUTF8ToNSString(emailToReauthenticate)
156 completion:^(ChromeIdentity* identity, NSError* error) {
157 [weakSelf handleIdentityAdded:identity
158 error:error
159 shouldSignIn:YES
160 viewController:viewController];
161 }];
162 }
163
164 - (void)addAccountWithCompletion:(CompletionCallback)completion
165 viewController:(UIViewController*)viewController {
166 completionCallback_.reset(completion, base::scoped_policy::RETAIN);
167 identityInteractionManager_ =
168 ios::GetChromeBrowserProvider()
169 ->GetChromeIdentityService()
170 ->NewChromeIdentityInteractionManager(browserState_, self);
171 base::WeakNSObject<SigninInteractionController> weakSelf(self);
172 [identityInteractionManager_
173 addAccountWithCompletion:^(ChromeIdentity* identity, NSError* error) {
174 [weakSelf handleIdentityAdded:identity
175 error:error
176 shouldSignIn:NO
177 viewController:viewController];
178 }];
179 }
180
181 #pragma mark - ChromeIdentityInteractionManager operations
182
183 - (void)handleIdentityAdded:(ChromeIdentity*)identity
184 error:(NSError*)error
185 shouldSignIn:(BOOL)shouldSignIn
186 viewController:(UIViewController*)viewController {
187 if (!identityInteractionManager_)
188 return;
189
190 if (error) {
191 // Filter out cancel and errors handled internally by ChromeIdentity.
192 if (!ShouldHandleSigninError(error)) {
193 [self runCompletionCallbackWithSuccess:NO executeCommand:nil];
194 return;
195 }
196
197 base::WeakNSObject<SigninInteractionController> weakSelf(self);
198 ProceduralBlock dismissAction = ^{
199 [weakSelf runCompletionCallbackWithSuccess:NO executeCommand:nil];
200 };
201
202 alertCoordinator_.reset([ios_internal::ErrorCoordinator(
203 error, dismissAction, viewController) retain]);
204 [alertCoordinator_ start];
205 return;
206 }
207 if (shouldSignIn) {
208 [self showSigninViewControllerWithIdentity:identity];
209 } else {
210 [self runCompletionCallbackWithSuccess:YES executeCommand:nil];
211 }
212 }
213
214 - (void)dismissPresentedViewControllersAnimated:(BOOL)animated
215 completion:(ProceduralBlock)completion {
216 if ([presentingViewController_ presentedViewController]) {
217 [presentingViewController_ dismissViewControllerAnimated:animated
218 completion:completion];
219 } else if (completion) {
220 completion();
221 }
222 interactionManagerDismissalIgnored_ = NO;
223 }
224
225 #pragma mark - ChromeIdentityInteractionManagerDelegate
226
227 - (void)interactionManager:(ChromeIdentityInteractionManager*)interactionManager
228 presentViewController:(UIViewController*)viewController
229 animated:(BOOL)animated
230 completion:(ProceduralBlock)completion {
231 [presentingViewController_ presentViewController:viewController
232 animated:animated
233 completion:completion];
234 }
235
236 - (void)interactionManager:(ChromeIdentityInteractionManager*)interactionManager
237 dismissViewControllerAnimated:(BOOL)animated
238 completion:(ProceduralBlock)completion {
239 // Avoid awkward double transitions by not dismissing
240 // identityInteractionManager_| if the signin view controller will be
241 // displayed on top of it. |identityInteractionManager_| will be dismissed
242 // when the signin view controller will be dismissed.
243 if ([interactionManager isCanceling]) {
244 [self dismissPresentedViewControllersAnimated:animated
245 completion:completion];
246 } else {
247 interactionManagerDismissalIgnored_ = YES;
248 if (completion) {
249 completion();
250 }
251 }
252 }
253
254 #pragma mark - ChromeSigninViewController operations
255
256 - (void)showSigninViewControllerWithIdentity:(ChromeIdentity*)signInIdentity {
257 signinViewController_.reset([[ChromeSigninViewController alloc]
258 initWithBrowserState:browserState_
259 isPresentedOnSettings:isPresentedOnSettings_
260 signInAccessPoint:signInAccessPoint_
261 signInIdentity:signInIdentity]);
262 [signinViewController_ setDelegate:self];
263 [signinViewController_
264 setModalPresentationStyle:UIModalPresentationFormSheet];
265 [signinViewController_
266 setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
267 signInIdentity_.reset([signInIdentity retain]);
268
269 UIViewController* presentingViewController = presentingViewController_;
270 if (identityInteractionManager_) {
271 // If |identityInteractionManager_| is currently displayed,
272 // |signinViewController_| is presented on top of it (instead of on top of
273 // |presentingViewController_|), to avoid an awkward transition (dismissing
274 // |identityInteractionManager_|, followed by presenting
275 // |signinViewController_|).
276 while (presentingViewController.presentedViewController) {
277 presentingViewController =
278 presentingViewController.presentedViewController;
279 }
280 }
281 [presentingViewController presentViewController:signinViewController_
282 animated:YES
283 completion:nil];
284 }
285
286 - (void)dismissSigninViewControllerWithSignInSuccess:(BOOL)success
287 executeCommand:
288 (GenericChromeCommand*)command {
289 DCHECK(signinViewController_);
290 if ((isCancelling_ && !isDismissing_) ||
291 ![presentingViewController_ presentedViewController]) {
292 [self runCompletionCallbackWithSuccess:success executeCommand:command];
293 return;
294 }
295 ProceduralBlock completion = ^{
296 [self runCompletionCallbackWithSuccess:success executeCommand:command];
297 };
298 [self dismissPresentedViewControllersAnimated:YES completion:completion];
299 }
300
301 #pragma mark - ChromeSigninViewControllerDelegate
302
303 - (void)willStartSignIn:(ChromeSigninViewController*)controller {
304 DCHECK_EQ(controller, signinViewController_.get());
305 }
306
307 - (void)willStartAddAccount:(ChromeSigninViewController*)controller {
308 DCHECK_EQ(controller, signinViewController_.get());
309 }
310
311 - (void)didSkipSignIn:(ChromeSigninViewController*)controller {
312 DCHECK_EQ(controller, signinViewController_.get());
313 [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
314 }
315
316 - (void)didSignIn:(ChromeSigninViewController*)controller {
317 DCHECK_EQ(controller, signinViewController_.get());
318 }
319
320 - (void)didUndoSignIn:(ChromeSigninViewController*)controller
321 identity:(ChromeIdentity*)identity {
322 DCHECK_EQ(controller, signinViewController_.get());
323 if ([signInIdentity_.get() isEqual:identity]) {
324 signInIdentity_.reset();
325 // This is best effort. If the operation fails, the account will be left on
326 // the device. The user will not be warned either as this call is
327 // asynchronous (but undo is not), the application might be in an unknown
328 // state when the forget identity operation finishes.
329 ios::GetChromeBrowserProvider()->GetChromeIdentityService()->ForgetIdentity(
330 identity, nil);
331 [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
332 }
333 }
334
335 - (void)didFailSignIn:(ChromeSigninViewController*)controller {
336 DCHECK_EQ(controller, signinViewController_.get());
337 [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
338 }
339
340 - (void)didAcceptSignIn:(ChromeSigninViewController*)controller
341 executeCommand:(GenericChromeCommand*)command {
342 DCHECK_EQ(controller, signinViewController_.get());
343 [self dismissSigninViewControllerWithSignInSuccess:YES
344 executeCommand:command];
345 }
346
347 #pragma mark - Utility methods
348
349 - (void)runCompletionCallbackWithSuccess:(BOOL)success
350 executeCommand:(GenericChromeCommand*)command {
351 // In order to avoid awkward double transitions, |identityInteractionManager_|
352 // is not dismissed when requested (except when canceling). However, in case
353 // of errors, |identityInteractionManager_| needs to be directly dismissed,
354 // which is done here.
355 if (interactionManagerDismissalIgnored_) {
356 [self dismissPresentedViewControllersAnimated:YES completion:nil];
357 }
358
359 identityInteractionManager_.reset();
360 signinViewController_.reset();
361 UIViewController* presentingViewController = presentingViewController_;
362 // Ensure self is not destroyed in the callbacks.
363 base::scoped_nsobject<SigninInteractionController> strongSelf([self retain]);
364 if (completionCallback_) {
365 completionCallback_.get()(success);
366 completionCallback_.reset();
367 }
368 strongSelf.reset();
369 if (command) {
370 [presentingViewController chromeExecuteCommand:command];
371 }
372 }
373
374 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698