chromium/ios/chrome/browser/download/ui_bundled/safari_download_coordinator.mm

// Copyright 2021 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/download/ui_bundled/safari_download_coordinator.h"

#import <SafariServices/SafariServices.h>

#import <memory>

#import "base/metrics/histogram_functions.h"
#import "base/metrics/histogram_macros.h"
#import "base/scoped_observation.h"
#import "base/strings/sys_string_conversions.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/download/model/safari_download_tab_helper.h"
#import "ios/chrome/browser/download/model/safari_download_tab_helper_delegate.h"
#import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/web_state_list/model/web_state_dependency_installer_bridge.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state_observer_bridge.h"
#import "ui/base/l10n/l10n_util.h"

const char kUmaDownloadCalendarFileUI[] = "Download.IOSDownloadCalendarFileUI";
const char kUmaDownloadMobileConfigFileUI[] =
    "Download.IOSDownloadMobileConfigFileUI";

@interface SafariDownloadCoordinator () <DependencyInstalling,
                                         SafariDownloadTabHelperDelegate,
                                         SFSafariViewControllerDelegate> {
  // Bridge which observes WebStateList and alerts this coordinator when this
  // needs to register the Mediator with a new WebState.
  std::unique_ptr<WebStateDependencyInstallerBridge> _dependencyInstallerBridge;
}

// Coordinator used to display modal alerts to the user.
@property(nonatomic, strong) AlertCoordinator* alertCoordinator;

// SFSafariViewController used to download files.
@property(nonatomic, strong) SFSafariViewController* safariViewController;

@end

@implementation SafariDownloadCoordinator

- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
                                   browser:(Browser*)browser {
  if ((self = [super initWithBaseViewController:baseViewController
                                        browser:browser])) {
    _dependencyInstallerBridge =
        std::make_unique<WebStateDependencyInstallerBridge>(
            self, browser->GetWebStateList());
  }
  return self;
}

- (void)stop {
  // Reset this observer manually. We want this to go out of scope now, to
  // ensure it detaches before `browser` and its WebStateList get destroyed.
  _dependencyInstallerBridge.reset();

  self.safariViewController = nil;
  [self dismissAlertCoordinator];
}

#pragma mark - Private

// Presents SFSafariViewController in order to download the file.
- (void)presentSFSafariViewController:(NSURL*)fileURL {
  self.safariViewController =
      [[SFSafariViewController alloc] initWithURL:fileURL];
  self.safariViewController.delegate = self;
  self.safariViewController.preferredBarTintColor =
      [UIColor colorNamed:kPrimaryBackgroundColor];

  [self.baseViewController presentViewController:self.safariViewController
                                        animated:YES
                                      completion:nil];
}

// Dismisses the alert coordinator.
- (void)dismissAlertCoordinator {
  [self.alertCoordinator stop];
  self.alertCoordinator = nil;
}
#pragma mark - DependencyInstalling methods

- (void)installDependencyForWebState:(web::WebState*)webState {
  SafariDownloadTabHelper::FromWebState(webState)->set_delegate(self);
}

- (void)uninstallDependencyForWebState:(web::WebState*)webState {
  SafariDownloadTabHelper::FromWebState(webState)->set_delegate(nil);
}

#pragma mark - SafariDownloadTabHelperDelegate

- (void)presentMobileConfigAlertFromURL:(NSURL*)fileURL {
  if (!fileURL || !fileURL.host.length) {
    return;
  }

  base::UmaHistogramEnumeration(kUmaDownloadMobileConfigFileUI,
                                SafariDownloadFileUI::kWarningAlertIsPresented);

  self.alertCoordinator = [[AlertCoordinator alloc]
      initWithBaseViewController:self.baseViewController
                         browser:self.browser
                           title:
                               l10n_util::GetNSString(
                                   IDS_IOS_DOWNLOAD_MOBILECONFIG_FILE_WARNING_TITLE)
                         message:
                             l10n_util::GetNSStringF(
                                 IDS_IOS_DOWNLOAD_MOBILECONFIG_FILE_WARNING_MESSAGE,
                                 base::SysNSStringToUTF16(fileURL.host))];

  __weak SafariDownloadCoordinator* weakSelf = self;
  [self.alertCoordinator
      addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
                action:^{
                  base::UmaHistogramEnumeration(
                      kUmaDownloadMobileConfigFileUI,
                      SafariDownloadFileUI::kWarningAlertIsDismissed);
                  [weakSelf dismissAlertCoordinator];
                }
                 style:UIAlertActionStyleCancel];

  [self.alertCoordinator
      addItemWithTitle:l10n_util::GetNSString(
                           IDS_IOS_DOWNLOAD_MOBILECONFIG_CONTINUE)
                action:^{
                  base::UmaHistogramEnumeration(
                      kUmaDownloadMobileConfigFileUI,
                      SafariDownloadFileUI::kSFSafariViewIsPresented);
                  [weakSelf presentSFSafariViewController:fileURL];
                  [weakSelf dismissAlertCoordinator];
                }
                 style:UIAlertActionStyleDefault];

  [self.alertCoordinator start];
}

- (void)presentCalendarAlertFromURL:(NSURL*)fileURL {
  if (!fileURL) {
    return;
  }

  base::UmaHistogramEnumeration(kUmaDownloadCalendarFileUI,
                                SafariDownloadFileUI::kWarningAlertIsPresented);

  self.alertCoordinator = [[AlertCoordinator alloc]
      initWithBaseViewController:self.baseViewController
                         browser:self.browser
                           title:
                               l10n_util::GetNSString(
                                   IDS_IOS_DOWNLOAD_CALENDAR_FILE_WARNING_TITLE)
                         message:
                             l10n_util::GetNSString(
                                 IDS_IOS_DOWNLOAD_CALENDAR_FILE_WARNING_MESSAGE)];

  __weak SafariDownloadCoordinator* weakSelf = self;
  [self.alertCoordinator
      addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
                action:^{
                  base::UmaHistogramEnumeration(
                      kUmaDownloadCalendarFileUI,
                      SafariDownloadFileUI::kWarningAlertIsDismissed);
                  [weakSelf dismissAlertCoordinator];
                }
                 style:UIAlertActionStyleCancel];

  [self.alertCoordinator
      addItemWithTitle:l10n_util::GetNSString(
                           IDS_IOS_DOWNLOAD_MOBILECONFIG_CONTINUE)
                action:^{
                  base::UmaHistogramEnumeration(
                      kUmaDownloadCalendarFileUI,
                      SafariDownloadFileUI::kSFSafariViewIsPresented);
                  [weakSelf presentSFSafariViewController:fileURL];
                  [weakSelf dismissAlertCoordinator];
                }
                 style:UIAlertActionStyleDefault];

  [self.alertCoordinator start];
}

#pragma mark - SFSafariViewControllerDelegate

- (void)safariViewControllerDidFinish:(SFSafariViewController*)controller {
  [self.baseViewController dismissViewControllerAnimated:true completion:nil];
}

@end