blob: 154a76982d2c41e068535aac1e39a2b29ecffa53 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// 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/authentication_flow_performer.h"
#import <MaterialComponents/MaterialSnackbar.h>
#import <memory>
#import "base/check_op.h"
#import "base/functional/bind.h"
#import "base/ios/block_types.h"
#import "base/metrics/user_metrics.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "base/time/time.h"
#import "base/timer/timer.h"
#import "components/policy/core/common/policy_pref_names.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/base/signin_pref_names.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/strings/grit/components_strings.h"
#import "google_apis/gaia/gaia_auth_util.h"
#import "google_apis/gaia/gaia_urls.h"
#import "ios/chrome/browser/policy/cloud/user_policy_signin_service.h"
#import "ios/chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
#import "ios/chrome/browser/policy/cloud/user_policy_switch.h"
#import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/public/commands/browsing_data_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h"
#import "ios/chrome/browser/shared/public/features/system_flags.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/constants.h"
#import "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/signin/system_identity.h"
#import "ios/chrome/browser/signin/system_identity_manager.h"
#import "ios/chrome/browser/sync/sync_setup_service.h"
#import "ios/chrome/browser/sync/sync_setup_service_factory.h"
#import "ios/chrome/browser/ui/authentication/authentication_constants.h"
#import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
#import "ios/chrome/browser/ui/settings/import_data_table_view_controller.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#import "ios/chrome/grit/ios_chromium_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state.h"
#import "services/network/public/cpp/shared_url_loader_factory.h"
#import "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using signin_ui::CompletionCallback;
namespace {
const int64_t kAuthenticationFlowTimeoutSeconds = 10;
NSString* const kAuthenticationSnackbarCategory =
@"AuthenticationSnackbarCategory";
} // namespace
@interface AuthenticationFlowPerformer () <ImportDataControllerDelegate,
SettingsNavigationControllerDelegate>
@end
@implementation AuthenticationFlowPerformer {
__weak id<AuthenticationFlowPerformerDelegate> _delegate;
AlertCoordinator* _alertCoordinator;
SettingsNavigationController* _navigationController;
std::unique_ptr<base::OneShotTimer> _watchdogTimer;
}
- (id<AuthenticationFlowPerformerDelegate>)delegate {
return _delegate;
}
- (instancetype)initWithDelegate:
(id<AuthenticationFlowPerformerDelegate>)delegate {
self = [super init];
if (self)
_delegate = delegate;
return self;
}
- (void)cancelAndDismissAnimated:(BOOL)animated {
[_alertCoordinator executeCancelHandler];
[_alertCoordinator stop];
if (_navigationController) {
[_navigationController cleanUpSettings];
_navigationController = nil;
[_delegate dismissPresentingViewControllerAnimated:animated completion:nil];
}
[self stopWatchdogTimer];
}
- (void)fetchManagedStatus:(ChromeBrowserState*)browserState
forIdentity:(id<SystemIdentity>)identity {
SystemIdentityManager* systemIdentityManager =
GetApplicationContext()->GetSystemIdentityManager();
if (NSString* hostedDomain =
systemIdentityManager->GetCachedHostedDomainForIdentity(identity)) {
[_delegate didFetchManagedStatus:hostedDomain];
return;
}
[self startWatchdogTimerForManagedStatus];
__weak AuthenticationFlowPerformer* weakSelf = self;
systemIdentityManager->GetHostedDomain(
identity, base::BindOnce(^(NSString* hostedDomain, NSError* error) {
[weakSelf handleGetHostedDomain:hostedDomain error:error];
}));
}
- (void)signInIdentity:(id<SystemIdentity>)identity
atAccessPoint:(signin_metrics::AccessPoint)accessPoint
withHostedDomain:(NSString*)hostedDomain
toBrowserState:(ChromeBrowserState*)browserState {
AuthenticationServiceFactory::GetForBrowserState(browserState)
->SignIn(identity, accessPoint);
}
- (void)signOutBrowserState:(ChromeBrowserState*)browserState {
__weak __typeof(_delegate) weakDelegate = _delegate;
AuthenticationServiceFactory::GetForBrowserState(browserState)
->SignOut(signin_metrics::ProfileSignout::kUserClickedSignoutSettings,
/*force_clear_browsing_data=*/false, ^{
[weakDelegate didSignOut];
});
}
- (void)signOutImmediatelyFromBrowserState:(ChromeBrowserState*)browserState {
AuthenticationServiceFactory::GetForBrowserState(browserState)
->SignOut(signin_metrics::ProfileSignout::kAbortSignin,
/*force_clear_browsing_data=*/false, nil);
}
- (void)promptMergeCaseForIdentity:(id<SystemIdentity>)identity
browser:(Browser*)browser
viewController:(UIViewController*)viewController {
DCHECK(browser);
ChromeBrowserState* browserState = browser->GetBrowserState();
signin::IdentityManager* identityManager =
IdentityManagerFactory::GetForBrowserState(browserState);
AuthenticationService* authenticationService =
AuthenticationServiceFactory::GetForBrowserState(browserState);
NSString* lastSyncingEmail =
authenticationService->GetPrimaryIdentity(signin::ConsentLevel::kSync)
.userEmail;
if (!lastSyncingEmail) {
// User is not opted in to sync, the current data comes may belong to the
// previously syncing account (if any).
lastSyncingEmail =
base::SysUTF8ToNSString(browserState->GetPrefs()->GetString(
prefs::kGoogleServicesLastUsername));
}
if (authenticationService->HasPrimaryIdentityManaged(
signin::ConsentLevel::kSync)) {
// If the current user is a managed account and sync is enabled, the sign-in
// needs to wipe the current data. We need to ask confirm from the user.
AccountInfo primaryAccountInfo = identityManager->FindExtendedAccountInfo(
identityManager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync));
DCHECK(!primaryAccountInfo.IsEmpty());
NSString* hostedDomain =
base::SysUTF8ToNSString(primaryAccountInfo.hosted_domain);
[self promptSwitchFromManagedEmail:lastSyncingEmail
withHostedDomain:hostedDomain
toEmail:identity.userEmail
viewController:viewController
browser:browser];
return;
}
_navigationController = [SettingsNavigationController
importDataControllerForBrowser:browser
delegate:self
importDataDelegate:self
fromEmail:lastSyncingEmail
toEmail:identity.userEmail];
[_delegate presentViewController:_navigationController
animated:YES
completion:nil];
}
- (void)clearDataFromBrowser:(Browser*)browser
commandHandler:(id<BrowsingDataCommands>)handler {
DCHECK(browser);
ChromeBrowserState* browserState = browser->GetBrowserState();
// The user needs to be signed out when clearing the data to avoid deleting
// data on server side too.
CHECK(!AuthenticationServiceFactory::GetForBrowserState(browserState)
->HasPrimaryIdentity(signin::ConsentLevel::kSignin));
// Workaround for crbug.com/1003578
//
// During the Chrome sign-in flow, if the user chooses to clear the cookies
// when switching their primary account, then the following actions take
// place (in the following order):
// 1. All cookies are cleared.
// 2. The user is signed in to Chrome (aka the primary account set)
// 2.a. All requests to Gaia page will include Mirror header.
// 2.b. Account reconcilor will rebuild the Gaia cookies having the Chrome
// primary account as the Gaia default web account.
//
// The Gaia sign-in webpage monitors changes to its cookies and reloads the
// page whenever they change. Reloading the webpage while the cookies are
// cleared and just before they are rebuilt seems to confuse WKWebView that
// ends up with a nil URL, which in turns translates in about:blank URL shown
// in the Omnibox.
//
// This CL works around this issue by waiting for 1 second between steps 1
// and 2 above to allow the WKWebView to initiate the reload after the
// cookies are cleared.
WebStateList* webStateList = browser->GetWebStateList();
web::WebState* activeWebState = webStateList->GetActiveWebState();
bool activeWebStateHasGaiaOrigin =
activeWebState &&
(activeWebState->GetVisibleURL().DeprecatedGetOriginAsURL() ==
GaiaUrls::GetInstance()->gaia_url());
int64_t dispatchDelaySecs = activeWebStateHasGaiaOrigin ? 1 : 0;
__weak __typeof(_delegate) weakDelegate = _delegate;
[handler removeBrowsingDataForBrowserState:browserState
timePeriod:browsing_data::TimePeriod::ALL_TIME
removeMask:BrowsingDataRemoveMask::REMOVE_ALL
completionBlock:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
dispatchDelaySecs *
NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
[weakDelegate didClearData];
});
}];
}
- (BOOL)shouldHandleMergeCaseForIdentity:(id<SystemIdentity>)identity
browserStatePrefs:(PrefService*)prefs {
const std::string lastSignedInGaiaId =
prefs->GetString(prefs::kGoogleServicesLastGaiaId);
if (!lastSignedInGaiaId.empty()) {
// Merge case exists if the id of the previously signed in account is
// different from the one of the account being signed in.
return lastSignedInGaiaId != base::SysNSStringToUTF8(identity.gaiaID);
}
// kGoogleServicesLastGaiaId pref might not have been populated yet,
// check the old kGoogleServicesLastUsername pref.
const std::string currentSignedInEmail =
base::SysNSStringToUTF8(identity.userEmail);
const std::string lastSignedInEmail =
prefs->GetString(prefs::kGoogleServicesLastUsername);
return !lastSignedInEmail.empty() &&
!gaia::AreEmailsSame(currentSignedInEmail, lastSignedInEmail);
}
- (void)showManagedConfirmationForHostedDomain:(NSString*)hostedDomain
viewController:(UIViewController*)viewController
browser:(Browser*)browser {
DCHECK(!_alertCoordinator);
BOOL userPolicyEnabled = policy::IsUserPolicyEnabled();
int titleID = userPolicyEnabled ? IDS_IOS_MANAGED_SYNC_TITLE
: IDS_IOS_MANAGED_SIGNIN_TITLE;
NSString* title = l10n_util::GetNSString(titleID);
int subtitleID = userPolicyEnabled
? IDS_IOS_MANAGED_SYNC_WITH_USER_POLICY_SUBTITLE
: IDS_IOS_MANAGED_SIGNIN_SUBTITLE;
NSString* subtitle = l10n_util::GetNSStringF(
subtitleID, base::SysNSStringToUTF16(hostedDomain));
NSString* acceptLabel =
l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON);
NSString* cancelLabel = l10n_util::GetNSString(IDS_CANCEL);
_alertCoordinator =
[[AlertCoordinator alloc] initWithBaseViewController:viewController
browser:browser
title:title
message:subtitle];
__weak AuthenticationFlowPerformer* weakSelf = self;
__weak AlertCoordinator* weakAlert = _alertCoordinator;
ProceduralBlock acceptBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
// TODO(crbug.com/1326767): Nullify the browser object in the
// AlertCoordinator when the coordinator is stopped to avoid using the
// browser object at that moment, in which case the browser object may have
// been deleted before the callback block is called. This is to avoid
// potential bad memory accesses.
Browser* alertedBrowser = weakAlert.browser;
if (alertedBrowser) {
PrefService* prefService = alertedBrowser->GetBrowserState()->GetPrefs();
// TODO(crbug.com/1325115): Remove this line once we determined that the
// notification isn't needed anymore.
[strongSelf updateUserPolicyNotificationStatusIfNeeded:prefService];
}
[strongSelf alertControllerDidDisappear:weakAlert];
[[strongSelf delegate] didAcceptManagedConfirmation];
};
ProceduralBlock cancelBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf alertControllerDidDisappear:weakAlert];
[[strongSelf delegate] didCancelManagedConfirmation];
};
[_alertCoordinator addItemWithTitle:cancelLabel
action:cancelBlock
style:UIAlertActionStyleCancel];
[_alertCoordinator addItemWithTitle:acceptLabel
action:acceptBlock
style:UIAlertActionStyleDefault];
[_alertCoordinator setCancelAction:cancelBlock];
[_alertCoordinator start];
}
- (void)showSnackbarWithSignInIdentity:(id<SystemIdentity>)identity
browser:(Browser*)browser {
DCHECK(browser);
base::WeakPtr<Browser> weakBrowser = browser->AsWeakPtr();
MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init];
action.handler = ^{
if (!weakBrowser.get()) {
return;
}
base::RecordAction(
base::UserMetricsAction("Mobile.Signin.SnackbarUndoTapped"));
ChromeBrowserState* browserState =
weakBrowser->GetBrowserState()->GetOriginalChromeBrowserState();
AuthenticationService* authService =
AuthenticationServiceFactory::GetForBrowserState(browserState);
if (authService->HasPrimaryIdentity(signin::ConsentLevel::kSignin)) {
authService->SignOut(
signin_metrics::ProfileSignout::kUserTappedUndoRightAfterSignIn,
/*force_clear_browsing_data=*/false, nil);
}
};
action.title = l10n_util::GetNSString(IDS_IOS_SIGNIN_SNACKBAR_UNDO);
action.accessibilityIdentifier = kSigninSnackbarUndo;
NSString* messageText =
l10n_util::GetNSStringF(IDS_IOS_SIGNIN_SNACKBAR_SIGNED_IN_AS,
base::SysNSStringToUTF16(identity.userEmail));
MDCSnackbarMessage* message =
[MDCSnackbarMessage messageWithText:messageText];
message.action = action;
message.category = kAuthenticationSnackbarCategory;
id<SnackbarCommands> handler =
HandlerForProtocol(browser->GetCommandDispatcher(), SnackbarCommands);
CHECK(handler);
TriggerHapticFeedbackForNotification(UINotificationFeedbackTypeSuccess);
[handler showSnackbarMessage:message];
}
- (void)showAuthenticationError:(NSError*)error
withCompletion:(ProceduralBlock)callback
viewController:(UIViewController*)viewController
browser:(Browser*)browser {
DCHECK(!_alertCoordinator);
_alertCoordinator = ErrorCoordinatorNoItem(error, viewController, browser);
__weak AuthenticationFlowPerformer* weakSelf = self;
__weak AlertCoordinator* weakAlert = _alertCoordinator;
ProceduralBlock dismissAction = ^{
[weakSelf alertControllerDidDisappear:weakAlert];
if (callback)
callback();
};
NSString* okButtonLabel = l10n_util::GetNSString(IDS_OK);
[_alertCoordinator addItemWithTitle:okButtonLabel
action:dismissAction
style:UIAlertActionStyleDefault];
[_alertCoordinator setCancelAction:dismissAction];
[_alertCoordinator start];
}
- (void)registerUserPolicy:(ChromeBrowserState*)browserState
forIdentity:(id<SystemIdentity>)identity {
// Should only fetch user policies when the feature is enabled.
DCHECK(policy::IsUserPolicyEnabled());
std::string userEmail = base::SysNSStringToUTF8(identity.userEmail);
CoreAccountId accountID =
IdentityManagerFactory::GetForBrowserState(browserState)
->PickAccountIdForAccount(base::SysNSStringToUTF8(identity.gaiaID),
userEmail);
policy::UserPolicySigninService* userPolicyService =
policy::UserPolicySigninServiceFactory::GetForBrowserState(browserState);
__weak __typeof(self) weakSelf = self;
[self startWatchdogTimerForUserPolicyRegistration];
userPolicyService->RegisterForPolicyWithAccountId(
userEmail, accountID,
base::BindOnce(^(const std::string& dmToken,
const std::string& clientID) {
if (![self stopWatchdogTimer]) {
// Watchdog timer has already fired, don't notify the delegate.
return;
}
[weakSelf.delegate
didRegisterForUserPolicyWithDMToken:base::SysUTF8ToNSString(dmToken)
clientID:base::SysUTF8ToNSString(
clientID)];
}));
}
- (void)fetchUserPolicy:(ChromeBrowserState*)browserState
withDmToken:(NSString*)dmToken
clientID:(NSString*)clientID
identity:(id<SystemIdentity>)identity {
// Should only fetch user policies when the feature is enabled.
DCHECK(policy::IsUserPolicyEnabled());
// Need a `dmToken` and a `clientID` to fetch user policies.
DCHECK([dmToken length] > 0);
DCHECK([clientID length] > 0);
policy::UserPolicySigninService* policy_service =
policy::UserPolicySigninServiceFactory::GetForBrowserState(browserState);
const std::string userEmail = base::SysNSStringToUTF8(identity.userEmail);
AccountId accountID =
AccountId::FromUserEmailGaiaId(gaia::CanonicalizeEmail(userEmail),
base::SysNSStringToUTF8(identity.gaiaID));
__weak __typeof(self) weakSelf = self;
[self startWatchdogTimerForUserPolicyFetch];
policy_service->FetchPolicyForSignedInUser(
accountID, base::SysNSStringToUTF8(dmToken),
base::SysNSStringToUTF8(clientID),
browserState->GetSharedURLLoaderFactory(),
base::BindOnce(^(bool success) {
if (![self stopWatchdogTimer]) {
// Watchdog timer has already fired, don't notify the delegate.
return;
}
[weakSelf.delegate didFetchUserPolicyWithSuccess:success];
}));
}
#pragma mark - ImportDataControllerDelegate
- (void)didChooseClearDataPolicy:(ImportDataTableViewController*)controller
shouldClearData:(ShouldClearData)shouldClearData {
DCHECK_NE(SHOULD_CLEAR_DATA_USER_CHOICE, shouldClearData);
if (shouldClearData == SHOULD_CLEAR_DATA_CLEAR_DATA) {
base::RecordAction(
base::UserMetricsAction("Signin_ImportDataPrompt_DontImport"));
} else {
base::RecordAction(
base::UserMetricsAction("Signin_ImportDataPrompt_ImportData"));
}
__weak AuthenticationFlowPerformer* weakSelf = self;
ProceduralBlock block = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
strongSelf->_navigationController = nil;
[[strongSelf delegate] didChooseClearDataPolicy:shouldClearData];
};
[_navigationController cleanUpSettings];
[_delegate dismissPresentingViewControllerAnimated:YES completion:block];
}
#pragma mark - SettingsNavigationControllerDelegate
- (void)closeSettings {
base::RecordAction(base::UserMetricsAction("Signin_ImportDataPrompt_Cancel"));
__weak AuthenticationFlowPerformer* weakSelf = self;
ProceduralBlock block = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
strongSelf->_navigationController = nil;
[[strongSelf delegate] didChooseCancel];
};
[_navigationController cleanUpSettings];
[_delegate dismissPresentingViewControllerAnimated:YES completion:block];
}
- (void)settingsWasDismissed {
base::RecordAction(base::UserMetricsAction("Signin_ImportDataPrompt_Cancel"));
[self.delegate didChooseCancel];
[_navigationController cleanUpSettings];
_navigationController = nil;
}
- (id<ApplicationCommands, BrowserCommands, BrowsingDataCommands>)
handlerForSettings {
NOTREACHED();
return nil;
}
- (id<ApplicationCommands>)handlerForApplicationCommands {
NOTREACHED();
return nil;
}
- (id<SnackbarCommands>)handlerForSnackbarCommands {
NOTREACHED();
return nil;
}
#pragma mark - Private
- (void)updateUserPolicyNotificationStatusIfNeeded:(PrefService*)prefService {
if (!policy::IsUserPolicyEnabled()) {
// Don't set the notification pref if the User Policy feature isn't
// enabled.
return;
}
prefService->SetBoolean(policy::policy_prefs::kUserPolicyNotificationWasShown,
true);
}
- (void)handleGetHostedDomain:(NSString*)hostedDomain error:(NSError*)error {
if (![self stopWatchdogTimer]) {
// Watchdog timer has already fired, don't notify the delegate.
return;
}
if (error) {
[_delegate didFailFetchManagedStatus:error];
return;
}
[_delegate didFetchManagedStatus:hostedDomain];
}
// Starts a Watchdog Timer that calls `timeoutBlock` on time out.
- (void)startWatchdogTimerWithTimeoutBlock:(ProceduralBlock)timeoutBlock {
DCHECK(!_watchdogTimer);
_watchdogTimer.reset(new base::OneShotTimer());
_watchdogTimer->Start(FROM_HERE,
base::Seconds(kAuthenticationFlowTimeoutSeconds),
base::BindOnce(timeoutBlock));
}
// Starts the watchdog timer with a timeout of
// `kAuthenticationFlowTimeoutSeconds` for the fetching managed status
// operation. It will notify `_delegate` of the failure unless
// `stopWatchdogTimer` is called before it times out.
- (void)startWatchdogTimerForManagedStatus {
__weak AuthenticationFlowPerformer* weakSelf = self;
ProceduralBlock timeoutBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf stopWatchdogTimer];
NSError* error = [NSError errorWithDomain:kAuthenticationErrorDomain
code:TIMED_OUT_FETCH_POLICY
userInfo:nil];
[strongSelf->_delegate didFailFetchManagedStatus:error];
};
[self startWatchdogTimerWithTimeoutBlock:timeoutBlock];
}
// Starts a Watchdog Timer that ends the user policy registration on time out.
- (void)startWatchdogTimerForUserPolicyRegistration {
__weak AuthenticationFlowPerformer* weakSelf = self;
ProceduralBlock timeoutBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf stopWatchdogTimer];
[strongSelf.delegate didRegisterForUserPolicyWithDMToken:@"" clientID:@""];
};
[self startWatchdogTimerWithTimeoutBlock:timeoutBlock];
}
// Starts a Watchdog Timer that ends the user policy fetch on time out.
- (void)startWatchdogTimerForUserPolicyFetch {
__weak AuthenticationFlowPerformer* weakSelf = self;
ProceduralBlock timeoutBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf stopWatchdogTimer];
[strongSelf->_delegate didFetchUserPolicyWithSuccess:NO];
};
[self startWatchdogTimerWithTimeoutBlock:timeoutBlock];
}
// Stops the watchdog timer, and doesn't call the `timeoutDelegateSelector`.
// Returns whether the watchdog was actually running.
- (BOOL)stopWatchdogTimer {
if (_watchdogTimer) {
_watchdogTimer->Stop();
_watchdogTimer.reset();
return YES;
}
return NO;
}
- (void)promptSwitchFromManagedEmail:(NSString*)managedEmail
withHostedDomain:(NSString*)hostedDomain
toEmail:(NSString*)toEmail
viewController:(UIViewController*)viewController
browser:(Browser*)browser {
DCHECK(!_alertCoordinator);
NSString* title = l10n_util::GetNSString(IDS_IOS_MANAGED_SWITCH_TITLE);
NSString* subtitle = l10n_util::GetNSStringF(
IDS_IOS_MANAGED_SWITCH_SUBTITLE, base::SysNSStringToUTF16(managedEmail),
base::SysNSStringToUTF16(toEmail),
base::SysNSStringToUTF16(hostedDomain));
NSString* acceptLabel =
l10n_util::GetNSString(IDS_IOS_MANAGED_SWITCH_ACCEPT_BUTTON);
NSString* cancelLabel = l10n_util::GetNSString(IDS_CANCEL);
_alertCoordinator =
[[AlertCoordinator alloc] initWithBaseViewController:viewController
browser:browser
title:title
message:subtitle];
__weak AuthenticationFlowPerformer* weakSelf = self;
__weak AlertCoordinator* weakAlert = _alertCoordinator;
ProceduralBlock acceptBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf alertControllerDidDisappear:weakAlert];
[[strongSelf delegate]
didChooseClearDataPolicy:SHOULD_CLEAR_DATA_CLEAR_DATA];
};
ProceduralBlock cancelBlock = ^{
AuthenticationFlowPerformer* strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf alertControllerDidDisappear:weakAlert];
[[strongSelf delegate] didChooseCancel];
};
[_alertCoordinator addItemWithTitle:cancelLabel
action:cancelBlock
style:UIAlertActionStyleCancel];
[_alertCoordinator addItemWithTitle:acceptLabel
action:acceptBlock
style:UIAlertActionStyleDefault];
[_alertCoordinator setCancelAction:cancelBlock];
[_alertCoordinator start];
}
// Callback for when the alert is dismissed.
- (void)alertControllerDidDisappear:(AlertCoordinator*)alertCoordinator {
if (_alertCoordinator != alertCoordinator) {
// Do not reset the `_alertCoordinator` if it has changed. This typically
// happens when the user taps on any of the actions on "Clear Data Before
// Syncing?" dialog, as the sign-in confirmation dialog is created before
// the "Clear Data Before Syncing?" dialog is dismissed.
return;
}
_alertCoordinator = nil;
}
@end