chromium/ios/chrome/browser/translate/model/chrome_ios_translate_client.mm

// 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/translate/model/chrome_ios_translate_client.h"

#import <utility>
#import <vector>

#import "base/check_op.h"
#import "base/feature_list.h"
#import "base/memory/ptr_util.h"
#import "base/notreached.h"
#import "components/infobars/core/infobar.h"
#import "components/language/core/browser/accept_languages_service.h"
#import "components/language/core/browser/language_model_manager.h"
#import "components/language/core/browser/pref_names.h"
#import "components/prefs/pref_service.h"
#import "components/translate/core/browser/page_translated_details.h"
#import "components/translate/core/browser/translate_infobar_delegate.h"
#import "components/translate/core/browser/translate_manager.h"
#import "components/translate/core/browser/translate_metrics_logger_impl.h"
#import "components/translate/core/browser/translate_step.h"
#import "components/translate/core/common/language_detection_details.h"
#import "ios/chrome/browser/infobars/model/infobar_ios.h"
#import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
#import "ios/chrome/browser/language/model/accept_languages_service_factory.h"
#import "ios/chrome/browser/language/model/language_model_manager_factory.h"
#import "ios/chrome/browser/language/model/url_language_histogram_factory.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/translate/model/language_detection_model_service_factory.h"
#import "ios/chrome/browser/translate/model/translate_model_service_factory.h"
#import "ios/chrome/browser/translate/model/translate_ranker_factory.h"
#import "ios/chrome/browser/translate/model/translate_service_ios.h"
#import "ios/chrome/grit/ios_theme_resources.h"
#import "ios/web/public/browser_state.h"
#import "ios/web/public/navigation/navigation_context.h"
#import "ios/web/public/web_state.h"
#import "third_party/metrics_proto/translate_event.pb.h"
#import "url/gurl.h"

ChromeIOSTranslateClient::ChromeIOSTranslateClient(web::WebState* web_state)
    : web_state_(web_state),
      translate_driver_(
          web_state,
          LanguageDetectionModelServiceFactory::GetForBrowserState(
              ChromeBrowserState::FromBrowserState(
                  web_state->GetBrowserState()))),
      translate_manager_(std::make_unique<translate::TranslateManager>(
          this,
          translate::TranslateRankerFactory::GetForBrowserState(
              ChromeBrowserState::FromBrowserState(
                  web_state->GetBrowserState())),
          LanguageModelManagerFactory::GetForBrowserState(
              ChromeBrowserState::FromBrowserState(
                  web_state->GetBrowserState()))
              ->GetPrimaryModel())) {
  translate_driver_.Initialize(
      UrlLanguageHistogramFactory::GetForBrowserState(
          ChromeBrowserState::FromBrowserState(web_state->GetBrowserState())),
      translate_manager_.get()),
      web_state_->AddObserver(this);
}

ChromeIOSTranslateClient::~ChromeIOSTranslateClient() {
  DCHECK(!web_state_);
}

// static
std::unique_ptr<translate::TranslatePrefs>
ChromeIOSTranslateClient::CreateTranslatePrefs(PrefService* prefs) {
  return std::unique_ptr<translate::TranslatePrefs>(
      new translate::TranslatePrefs(prefs));
}

translate::TranslateManager* ChromeIOSTranslateClient::GetTranslateManager() {
  return translate_manager_.get();
}

// TranslateClient implementation:

std::unique_ptr<infobars::InfoBar> ChromeIOSTranslateClient::CreateInfoBar(
    std::unique_ptr<translate::TranslateInfoBarDelegate> delegate) const {
  bool skip_banner = delegate->translate_step() ==
                     translate::TranslateStep::TRANSLATE_STEP_TRANSLATING;
  return std::make_unique<InfoBarIOS>(InfobarType::kInfobarTypeTranslate,
                                      std::move(delegate), skip_banner);
}

bool ChromeIOSTranslateClient::ShowTranslateUI(
    translate::TranslateStep step,
    const std::string& source_language,
    const std::string& target_language,
    translate::TranslateErrors error_type,
    bool triggered_from_menu) {
  DCHECK(web_state_);
  if (error_type != translate::TranslateErrors::NONE) {
    step = translate::TRANSLATE_STEP_TRANSLATE_ERROR;
  }

  // Infobar UI.
  translate::TranslateInfoBarDelegate::Create(
      step != translate::TRANSLATE_STEP_BEFORE_TRANSLATE || triggered_from_menu,
      translate_manager_->GetWeakPtr(),
      InfoBarManagerImpl::FromWebState(web_state_), step, source_language,
      target_language, error_type, triggered_from_menu);

  return true;
}

translate::IOSTranslateDriver* ChromeIOSTranslateClient::GetTranslateDriver() {
  return &translate_driver_;
}

PrefService* ChromeIOSTranslateClient::GetPrefs() {
  DCHECK(web_state_);
  ChromeBrowserState* chrome_browser_state =
      ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState());
  return chrome_browser_state->GetOriginalChromeBrowserState()->GetPrefs();
}

std::unique_ptr<translate::TranslatePrefs>
ChromeIOSTranslateClient::GetTranslatePrefs() {
  DCHECK(web_state_);
  ChromeBrowserState* chrome_browser_state =
      ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState());
  return CreateTranslatePrefs(chrome_browser_state->GetPrefs());
}

language::AcceptLanguagesService*
ChromeIOSTranslateClient::GetAcceptLanguagesService() {
  DCHECK(web_state_);
  return AcceptLanguagesServiceFactory::GetForBrowserState(
      ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState()));
}

int ChromeIOSTranslateClient::GetInfobarIconID() const {
  return IDR_IOS_INFOBAR_TRANSLATE;
}

bool ChromeIOSTranslateClient::IsTranslatableURL(const GURL& url) {
  return TranslateServiceIOS::IsTranslatableURL(url);
}

void ChromeIOSTranslateClient::DidStartNavigation(
    web::WebState* web_state,
    web::NavigationContext* navigation_context) {
  if (navigation_context->IsSameDocument()) {
    return;
  }

  DidPageLoadComplete();
  if (!IsTranslatableURL(navigation_context->GetUrl())) {
    // If URL is not translatable, do not record metrics as this would skew the
    // data.
    translate_metrics_logger_.reset();
    translate_manager_->RegisterTranslateMetricsLogger(nullptr);
    return;
  }
  // Lifetime of TranslateMetricsLogger should be each page load. So, we need to
  // detect the page load completion, i.e. the tab was closed, new navigation
  // replaced the page load, etc, and clear the logger.
  translate_metrics_logger_ =
      std::make_unique<translate::TranslateMetricsLoggerImpl>(
          translate_manager_->GetWeakPtr());
  translate_metrics_logger_->OnPageLoadStart(web_state->IsVisible());
}

void ChromeIOSTranslateClient::DidFinishNavigation(
    web::WebState* web_state,
    web::NavigationContext* navigation_context) {
  if (navigation_context->GetError()) {
    translate_metrics_logger_.reset();
    return;
  }

  if (!navigation_context->IsSameDocument() && translate_metrics_logger_) {
    translate_metrics_logger_->SetUkmSourceId(
        translate_driver_.GetUkmSourceId());
  }
}

void ChromeIOSTranslateClient::WasShown(web::WebState* web_state) {
  if (translate_metrics_logger_) {
    translate_metrics_logger_->OnForegroundChange(true);
  }
}

void ChromeIOSTranslateClient::WasHidden(web::WebState* web_state) {
  if (translate_metrics_logger_) {
    translate_metrics_logger_->OnForegroundChange(false);
  }
}

void ChromeIOSTranslateClient::WebStateDestroyed(web::WebState* web_state) {
  DCHECK_EQ(web_state_, web_state);
  web_state_->RemoveObserver(this);
  web_state_ = nullptr;

  DidPageLoadComplete();

  // Translation process can be interrupted.
  // Destroying the TranslateManager now guarantees that it never has to deal
  // with nullptr WebState.
  translate_manager_.reset();
}

void ChromeIOSTranslateClient::DidPageLoadComplete() {
  if (translate_metrics_logger_) {
    translate_metrics_logger_->RecordMetrics(true);
    translate_metrics_logger_.reset();
  }
}

WEB_STATE_USER_DATA_KEY_IMPL(ChromeIOSTranslateClient)