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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/authentication/signin_interaction_controller.mm
diff --git a/ios/chrome/browser/ui/authentication/signin_interaction_controller.mm b/ios/chrome/browser/ui/authentication/signin_interaction_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..a1fc182f2132c3befdd9cb238e696ca8ff9dafdc
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin_interaction_controller.mm
@@ -0,0 +1,374 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin_interaction_controller.h"
+
+#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/scoped_block.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/signin/authentication_service.h"
+#include "ios/chrome/browser/signin/authentication_service_factory.h"
+#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#import "ios/chrome/browser/signin/signin_util.h"
+#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
+#import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
+#import "ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h"
+#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity_interaction_manager.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+
+using signin_ui::CompletionCallback;
+
+@interface SigninInteractionController ()<
+ ChromeIdentityInteractionManagerDelegate,
+ ChromeSigninViewControllerDelegate> {
+ ios::ChromeBrowserState* browserState_;
+ signin_metrics::AccessPoint signInAccessPoint_;
+ base::scoped_nsobject<UIViewController> presentingViewController_;
+ BOOL isPresentedOnSettings_;
+ BOOL isCancelling_;
+ BOOL isDismissing_;
+ BOOL interactionManagerDismissalIgnored_;
+ base::scoped_nsobject<AlertCoordinator> alertCoordinator_;
+ base::mac::ScopedBlock<CompletionCallback> completionCallback_;
+ base::scoped_nsobject<ChromeSigninViewController> signinViewController_;
+ base::scoped_nsobject<ChromeIdentityInteractionManager>
+ identityInteractionManager_;
+ base::scoped_nsobject<ChromeIdentity> signInIdentity_;
+}
+@end
+
+@implementation SigninInteractionController
+
+- (id)init {
+ NOTREACHED();
+ return nil;
+}
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
+ presentingViewController:(UIViewController*)presentingViewController
+ isPresentedOnSettings:(BOOL)isPresentedOnSettings
+ signInAccessPoint:(signin_metrics::AccessPoint)accessPoint {
+ self = [super init];
+ if (self) {
+ DCHECK(browserState);
+ DCHECK(presentingViewController);
+ browserState_ = browserState;
+ presentingViewController_.reset([presentingViewController retain]);
+ isPresentedOnSettings_ = isPresentedOnSettings;
+ signInAccessPoint_ = accessPoint;
+ }
+ return self;
+}
+
+- (void)cancel {
+ // Cancelling and dismissing the |identityInteractionManager_| may call the
+ // |completionCallback_| which could lead to |self| being released before the
+ // end of this method. |self| is retained here to prevent this from happening.
+ base::scoped_nsobject<SigninInteractionController> strongSelf([self retain]);
+ isCancelling_ = YES;
+ [alertCoordinator_ executeCancelHandler];
+ [alertCoordinator_ stop];
+ [identityInteractionManager_ cancelAndDismissAnimated:NO];
+ [signinViewController_ cancel];
+ isCancelling_ = NO;
+}
+
+- (void)cancelAndDismiss {
+ isDismissing_ = YES;
+ [self cancel];
+ isDismissing_ = NO;
+}
+
+- (void)signInWithCompletion:(CompletionCallback)completion
+ viewController:(UIViewController*)viewController {
+ signin_metrics::LogSigninAccessPointStarted(signInAccessPoint_);
+ completionCallback_.reset(completion, base::scoped_policy::RETAIN);
+ if (ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->HasIdentities()) {
+ DCHECK(!signinViewController_);
+ [self showSigninViewControllerWithIdentity:nil];
+ } else {
+ identityInteractionManager_ =
+ ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->NewChromeIdentityInteractionManager(browserState_, self);
+ if (!identityInteractionManager_) {
+ // Abort sign-in if the ChromeIdentityInteractionManager returned is
+ // nil (this can happen when the iOS internal provider is not used).
+ [self runCompletionCallbackWithSuccess:NO executeCommand:nil];
+ return;
+ }
+
+ base::WeakNSObject<SigninInteractionController> weakSelf(self);
+ [identityInteractionManager_
+ addAccountWithCompletion:^(ChromeIdentity* identity, NSError* error) {
+ [weakSelf handleIdentityAdded:identity
+ error:error
+ shouldSignIn:YES
+ viewController:viewController];
+ }];
+ }
+}
+
+- (void)reAuthenticateWithCompletion:(CompletionCallback)completion
+ viewController:(UIViewController*)viewController {
+ signin_metrics::LogSigninAccessPointStarted(signInAccessPoint_);
+ completionCallback_.reset(completion, base::scoped_policy::RETAIN);
+ AccountInfo accountInfo =
+ ios::SigninManagerFactory::GetForBrowserState(browserState_)
+ ->GetAuthenticatedAccountInfo();
+ std::string emailToReauthenticate = accountInfo.email;
+ std::string idToReauthenticate = accountInfo.gaia;
+ if (emailToReauthenticate.empty() || idToReauthenticate.empty()) {
+ // This corresponds to a re-authenticate request after the user was signed
+ // out. This corresponds to the case where the identity was removed as a
+ // result of the permissions being removed on the server or the identity
+ // being removed from another app.
+ //
+ // Simply use the the last signed-in user email in this case and go though
+ // the entire sign-in flow as sync needs to be configured.
+ emailToReauthenticate = browserState_->GetPrefs()->GetString(
+ prefs::kGoogleServicesLastUsername);
+ idToReauthenticate = browserState_->GetPrefs()->GetString(
+ prefs::kGoogleServicesLastAccountId);
+ }
+ DCHECK(!emailToReauthenticate.empty());
+ DCHECK(!idToReauthenticate.empty());
+ identityInteractionManager_ =
+ ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->NewChromeIdentityInteractionManager(browserState_, self);
+ base::WeakNSObject<SigninInteractionController> weakSelf(self);
+ [identityInteractionManager_
+ reauthenticateUserWithID:base::SysUTF8ToNSString(idToReauthenticate)
+ email:base::SysUTF8ToNSString(emailToReauthenticate)
+ completion:^(ChromeIdentity* identity, NSError* error) {
+ [weakSelf handleIdentityAdded:identity
+ error:error
+ shouldSignIn:YES
+ viewController:viewController];
+ }];
+}
+
+- (void)addAccountWithCompletion:(CompletionCallback)completion
+ viewController:(UIViewController*)viewController {
+ completionCallback_.reset(completion, base::scoped_policy::RETAIN);
+ identityInteractionManager_ =
+ ios::GetChromeBrowserProvider()
+ ->GetChromeIdentityService()
+ ->NewChromeIdentityInteractionManager(browserState_, self);
+ base::WeakNSObject<SigninInteractionController> weakSelf(self);
+ [identityInteractionManager_
+ addAccountWithCompletion:^(ChromeIdentity* identity, NSError* error) {
+ [weakSelf handleIdentityAdded:identity
+ error:error
+ shouldSignIn:NO
+ viewController:viewController];
+ }];
+}
+
+#pragma mark - ChromeIdentityInteractionManager operations
+
+- (void)handleIdentityAdded:(ChromeIdentity*)identity
+ error:(NSError*)error
+ shouldSignIn:(BOOL)shouldSignIn
+ viewController:(UIViewController*)viewController {
+ if (!identityInteractionManager_)
+ return;
+
+ if (error) {
+ // Filter out cancel and errors handled internally by ChromeIdentity.
+ if (!ShouldHandleSigninError(error)) {
+ [self runCompletionCallbackWithSuccess:NO executeCommand:nil];
+ return;
+ }
+
+ base::WeakNSObject<SigninInteractionController> weakSelf(self);
+ ProceduralBlock dismissAction = ^{
+ [weakSelf runCompletionCallbackWithSuccess:NO executeCommand:nil];
+ };
+
+ alertCoordinator_.reset([ios_internal::ErrorCoordinator(
+ error, dismissAction, viewController) retain]);
+ [alertCoordinator_ start];
+ return;
+ }
+ if (shouldSignIn) {
+ [self showSigninViewControllerWithIdentity:identity];
+ } else {
+ [self runCompletionCallbackWithSuccess:YES executeCommand:nil];
+ }
+}
+
+- (void)dismissPresentedViewControllersAnimated:(BOOL)animated
+ completion:(ProceduralBlock)completion {
+ if ([presentingViewController_ presentedViewController]) {
+ [presentingViewController_ dismissViewControllerAnimated:animated
+ completion:completion];
+ } else if (completion) {
+ completion();
+ }
+ interactionManagerDismissalIgnored_ = NO;
+}
+
+#pragma mark - ChromeIdentityInteractionManagerDelegate
+
+- (void)interactionManager:(ChromeIdentityInteractionManager*)interactionManager
+ presentViewController:(UIViewController*)viewController
+ animated:(BOOL)animated
+ completion:(ProceduralBlock)completion {
+ [presentingViewController_ presentViewController:viewController
+ animated:animated
+ completion:completion];
+}
+
+- (void)interactionManager:(ChromeIdentityInteractionManager*)interactionManager
+ dismissViewControllerAnimated:(BOOL)animated
+ completion:(ProceduralBlock)completion {
+ // Avoid awkward double transitions by not dismissing
+ // identityInteractionManager_| if the signin view controller will be
+ // displayed on top of it. |identityInteractionManager_| will be dismissed
+ // when the signin view controller will be dismissed.
+ if ([interactionManager isCanceling]) {
+ [self dismissPresentedViewControllersAnimated:animated
+ completion:completion];
+ } else {
+ interactionManagerDismissalIgnored_ = YES;
+ if (completion) {
+ completion();
+ }
+ }
+}
+
+#pragma mark - ChromeSigninViewController operations
+
+- (void)showSigninViewControllerWithIdentity:(ChromeIdentity*)signInIdentity {
+ signinViewController_.reset([[ChromeSigninViewController alloc]
+ initWithBrowserState:browserState_
+ isPresentedOnSettings:isPresentedOnSettings_
+ signInAccessPoint:signInAccessPoint_
+ signInIdentity:signInIdentity]);
+ [signinViewController_ setDelegate:self];
+ [signinViewController_
+ setModalPresentationStyle:UIModalPresentationFormSheet];
+ [signinViewController_
+ setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
+ signInIdentity_.reset([signInIdentity retain]);
+
+ UIViewController* presentingViewController = presentingViewController_;
+ if (identityInteractionManager_) {
+ // If |identityInteractionManager_| is currently displayed,
+ // |signinViewController_| is presented on top of it (instead of on top of
+ // |presentingViewController_|), to avoid an awkward transition (dismissing
+ // |identityInteractionManager_|, followed by presenting
+ // |signinViewController_|).
+ while (presentingViewController.presentedViewController) {
+ presentingViewController =
+ presentingViewController.presentedViewController;
+ }
+ }
+ [presentingViewController presentViewController:signinViewController_
+ animated:YES
+ completion:nil];
+}
+
+- (void)dismissSigninViewControllerWithSignInSuccess:(BOOL)success
+ executeCommand:
+ (GenericChromeCommand*)command {
+ DCHECK(signinViewController_);
+ if ((isCancelling_ && !isDismissing_) ||
+ ![presentingViewController_ presentedViewController]) {
+ [self runCompletionCallbackWithSuccess:success executeCommand:command];
+ return;
+ }
+ ProceduralBlock completion = ^{
+ [self runCompletionCallbackWithSuccess:success executeCommand:command];
+ };
+ [self dismissPresentedViewControllersAnimated:YES completion:completion];
+}
+
+#pragma mark - ChromeSigninViewControllerDelegate
+
+- (void)willStartSignIn:(ChromeSigninViewController*)controller {
+ DCHECK_EQ(controller, signinViewController_.get());
+}
+
+- (void)willStartAddAccount:(ChromeSigninViewController*)controller {
+ DCHECK_EQ(controller, signinViewController_.get());
+}
+
+- (void)didSkipSignIn:(ChromeSigninViewController*)controller {
+ DCHECK_EQ(controller, signinViewController_.get());
+ [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
+}
+
+- (void)didSignIn:(ChromeSigninViewController*)controller {
+ DCHECK_EQ(controller, signinViewController_.get());
+}
+
+- (void)didUndoSignIn:(ChromeSigninViewController*)controller
+ identity:(ChromeIdentity*)identity {
+ DCHECK_EQ(controller, signinViewController_.get());
+ if ([signInIdentity_.get() isEqual:identity]) {
+ signInIdentity_.reset();
+ // This is best effort. If the operation fails, the account will be left on
+ // the device. The user will not be warned either as this call is
+ // asynchronous (but undo is not), the application might be in an unknown
+ // state when the forget identity operation finishes.
+ ios::GetChromeBrowserProvider()->GetChromeIdentityService()->ForgetIdentity(
+ identity, nil);
+ [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
+ }
+}
+
+- (void)didFailSignIn:(ChromeSigninViewController*)controller {
+ DCHECK_EQ(controller, signinViewController_.get());
+ [self dismissSigninViewControllerWithSignInSuccess:NO executeCommand:nil];
+}
+
+- (void)didAcceptSignIn:(ChromeSigninViewController*)controller
+ executeCommand:(GenericChromeCommand*)command {
+ DCHECK_EQ(controller, signinViewController_.get());
+ [self dismissSigninViewControllerWithSignInSuccess:YES
+ executeCommand:command];
+}
+
+#pragma mark - Utility methods
+
+- (void)runCompletionCallbackWithSuccess:(BOOL)success
+ executeCommand:(GenericChromeCommand*)command {
+ // In order to avoid awkward double transitions, |identityInteractionManager_|
+ // is not dismissed when requested (except when canceling). However, in case
+ // of errors, |identityInteractionManager_| needs to be directly dismissed,
+ // which is done here.
+ if (interactionManagerDismissalIgnored_) {
+ [self dismissPresentedViewControllersAnimated:YES completion:nil];
+ }
+
+ identityInteractionManager_.reset();
+ signinViewController_.reset();
+ UIViewController* presentingViewController = presentingViewController_;
+ // Ensure self is not destroyed in the callbacks.
+ base::scoped_nsobject<SigninInteractionController> strongSelf([self retain]);
+ if (completionCallback_) {
+ completionCallback_.get()(success);
+ completionCallback_.reset();
+ }
+ strongSelf.reset();
+ if (command) {
+ [presentingViewController chromeExecuteCommand:command];
+ }
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698