// Copyright 2020 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/settings/safety_check/safety_check_coordinator.h"
#import "base/apple/foundation_util.h"
#import "base/memory/scoped_refptr.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/histogram_macros.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/sys_string_conversions.h"
#import "components/password_manager/core/browser/ui/password_check_referrer.h"
#import "components/safe_browsing/core/common/features.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/passwords/model/password_checkup_utils.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/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_coordinator.h"
#import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_coordinator.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
#import "ios/chrome/browser/ui/settings/safety_check/safety_check_ui_swift.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#import "ios/chrome/common/ui/elements/popover_label_view_controller.h"
#import "net/base/apple/url_conversions.h"
#import "url/gurl.h"
using password_manager::WarningType;
@interface SafetyCheckCoordinator () <
PasswordCheckupCoordinatorDelegate,
PopoverLabelViewControllerDelegate,
PrivacySafeBrowsingCoordinatorDelegate,
SafetyCheckNavigationCommands,
SafetyCheckTableViewControllerPresentationDelegate>
// Safety check mediator.
@property(nonatomic, strong) SafetyCheckMediator* mediator;
// The container view controller.
@property(nonatomic, strong) SafetyCheckTableViewController* viewController;
// Coordinator for Password Checkup.
@property(nonatomic, strong)
PasswordCheckupCoordinator* passwordCheckupCoordinator;
// Dispatcher which can handle changing passwords on sites.
@property(nonatomic, strong) id<ApplicationCommands> handler;
// Coordinator for the Privacy and Security screen (SafeBrowsing toggle
// location).
@property(nonatomic, strong)
PrivacySafeBrowsingCoordinator* privacySafeBrowsingCoordinator;
// Where in the app the Safety Check was requested from.
@property(nonatomic, assign) password_manager::PasswordCheckReferrer referrer;
// Popover view controller with error information.
@property(nonatomic, strong)
PopoverLabelViewController* errorInfoPopoverViewController;
@end
@implementation SafetyCheckCoordinator
@synthesize baseNavigationController = _baseNavigationController;
- (instancetype)
initWithBaseNavigationController:
(UINavigationController*)navigationController
browser:(Browser*)browser
referrer:(password_manager::PasswordCheckReferrer)
referrer {
self = [super initWithBaseViewController:navigationController
browser:browser];
if (self) {
_baseNavigationController = navigationController;
_handler = HandlerForProtocol(self.browser->GetCommandDispatcher(),
ApplicationCommands);
_referrer = referrer;
}
return self;
}
- (void)startCheckIfNotRunning {
[self.mediator startCheckIfNotRunning];
}
#pragma mark - ChromeCoordinator
- (void)start {
SafetyCheckTableViewController* viewController =
[[SafetyCheckTableViewController alloc]
initWithStyle:ChromeTableViewStyle()];
self.viewController = viewController;
scoped_refptr<IOSChromePasswordCheckManager> passwordCheckManager =
IOSChromePasswordCheckManagerFactory::GetForBrowserState(
self.browser->GetBrowserState());
self.mediator = [[SafetyCheckMediator alloc]
initWithUserPrefService:self.browser->GetBrowserState()->GetPrefs()
localPrefService:GetApplicationContext()->GetLocalState()
passwordCheckManager:passwordCheckManager
authService:AuthenticationServiceFactory::GetForBrowserState(
self.browser->GetBrowserState())
syncService:SyncServiceFactory::GetForBrowserState(
self.browser->GetBrowserState())
referrer:_referrer];
self.mediator.consumer = self.viewController;
self.mediator.handler = self;
self.viewController.serviceDelegate = self.mediator;
self.viewController.presentationDelegate = self;
DCHECK(self.baseNavigationController);
[self.baseNavigationController pushViewController:self.viewController
animated:YES];
}
- (void)stop {
// If the Safe Browsing Settings page was accessed through the Safe
// Browsing row of the safety check, we need to explicity stop the
// privacySafeBrowsingCoordinator before closing the settings window.
[self.privacySafeBrowsingCoordinator stop];
self.privacySafeBrowsingCoordinator.delegate = nil;
self.privacySafeBrowsingCoordinator = nil;
[self.passwordCheckupCoordinator stop];
self.passwordCheckupCoordinator.delegate = nil;
self.passwordCheckupCoordinator = nil;
}
#pragma mark - SafetyCheckTableViewControllerPresentationDelegate
- (void)safetyCheckTableViewControllerDidRemove:
(SafetyCheckTableViewController*)controller {
DCHECK_EQ(self.viewController, controller);
[self.delegate safetyCheckCoordinatorDidRemove:self];
}
#pragma mark - PopoverLabelViewControllerDelegate
- (void)didTapLinkURL:(NSURL*)URL {
GURL convertedURL = net::GURLWithNSURL(URL);
const GURL safeBrowsingURL(
base::SysNSStringToUTF8(kSafeBrowsingSafetyCheckStringURL));
// Take the user to Sync and Google Services page in Bling instead of desktop
// settings.
if (convertedURL == safeBrowsingURL) {
[self.errorInfoPopoverViewController
dismissViewControllerAnimated:YES
completion:^{
[self showSafeBrowsingPreferencePage];
}];
return;
}
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:convertedURL];
[self.handler closeSettingsUIAndOpenURL:command];
}
#pragma mark - SafetyCheckNavigationCommands
- (void)showPasswordCheckupPage {
DUMP_WILL_BE_CHECK(!self.passwordCheckupCoordinator);
self.passwordCheckupCoordinator = [[PasswordCheckupCoordinator alloc]
initWithBaseNavigationController:self.baseNavigationController
browser:self.browser
reauthModule:nil
referrer:password_manager::PasswordCheckReferrer::
kSafetyCheck];
self.passwordCheckupCoordinator.delegate = self;
[self.passwordCheckupCoordinator start];
}
- (void)showErrorInfoFrom:(UIButton*)buttonView
withText:(NSAttributedString*)text {
self.errorInfoPopoverViewController =
[[PopoverLabelViewController alloc] initWithPrimaryAttributedString:text
secondaryAttributedString:nil];
self.errorInfoPopoverViewController.delegate = self;
self.errorInfoPopoverViewController.popoverPresentationController.sourceView =
buttonView;
self.errorInfoPopoverViewController.popoverPresentationController.sourceRect =
buttonView.bounds;
self.errorInfoPopoverViewController.popoverPresentationController
.permittedArrowDirections = UIPopoverArrowDirectionAny;
[self.viewController presentViewController:self.errorInfoPopoverViewController
animated:YES
completion:nil];
}
- (void)showUpdateAtLocation:(NSString*)location {
if (!location) {
NOTREACHED_IN_MIGRATION();
return;
}
const GURL url(base::SysNSStringToUTF8(location));
OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:url];
[self.handler closeSettingsUIAndOpenURL:command];
}
- (void)showSafeBrowsingPreferencePage {
DCHECK(!self.privacySafeBrowsingCoordinator);
base::RecordAction(
base::UserMetricsAction("Settings.SafetyCheck.ManageSafeBrowsing"));
base::UmaHistogramEnumeration("Settings.SafetyCheck.Interactions",
SafetyCheckInteractions::kSafeBrowsingManage);
self.privacySafeBrowsingCoordinator = [[PrivacySafeBrowsingCoordinator alloc]
initWithBaseNavigationController:self.baseNavigationController
browser:self.browser];
self.privacySafeBrowsingCoordinator.delegate = self;
[self.privacySafeBrowsingCoordinator start];
}
- (void)showManagedInfoFrom:(UIButton*)buttonView {
EnterpriseInfoPopoverViewController* bubbleViewController =
[[EnterpriseInfoPopoverViewController alloc] initWithEnterpriseName:nil];
[self.viewController presentViewController:bubbleViewController
animated:YES
completion:nil];
// Disable the button when showing the bubble.
// The button will be enabled when close the bubble in
// (void)popoverPresentationControllerDidDismissPopover: of
// EnterpriseInfoPopoverViewController.
buttonView.enabled = NO;
// Set the anchor and arrow direction of the bubble.
bubbleViewController.popoverPresentationController.sourceView = buttonView;
bubbleViewController.popoverPresentationController.sourceRect =
buttonView.bounds;
bubbleViewController.popoverPresentationController.permittedArrowDirections =
UIPopoverArrowDirectionAny;
}
#pragma mark - PasswordCheckupCoordinatorDelegate
- (void)passwordCheckupCoordinatorDidRemove:
(PasswordCheckupCoordinator*)coordinator {
DCHECK_EQ(self.passwordCheckupCoordinator, coordinator);
[self.passwordCheckupCoordinator stop];
self.passwordCheckupCoordinator.delegate = nil;
self.passwordCheckupCoordinator = nil;
}
#pragma mark - PasswordManagerReauthenticationDelegate
- (void)dismissPasswordManagerAfterFailedReauthentication {
// Pop everything up to the Safety Check page.
// When there is content presented, don't animate the dismissal of the view
// controllers in the navigation controller to prevent revealing passwords
// when the presented content is the one covered by the reauthentication UI.
UINavigationController* navigationController = self.baseNavigationController;
UIViewController* topViewController = navigationController.topViewController;
UIViewController* presentedViewController =
topViewController.presentedViewController;
[navigationController popToViewController:_viewController
animated:presentedViewController == nil];
[presentedViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
}
#pragma mark - PrivacySafeBrowsingCoordinatorDelegate
- (void)privacySafeBrowsingCoordinatorDidRemove:
(PrivacySafeBrowsingCoordinator*)coordinator {
DCHECK_EQ(_privacySafeBrowsingCoordinator, coordinator);
[self.privacySafeBrowsingCoordinator stop];
self.privacySafeBrowsingCoordinator.delegate = nil;
self.privacySafeBrowsingCoordinator = nil;
}
@end