chromium/chrome/browser/ui/android/infobars/translate_compact_infobar.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/android/infobars/translate_compact_infobar.h"

#include <stddef.h>

#include <memory>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/types/cxx23_to_underlying.h"
#include "chrome/browser/android/tab_android.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/translate/core/browser/translate_infobar_delegate.h"
#include "components/translate/core/browser/translate_metrics_logger.h"
#include "content/public/browser/browser_context.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/TranslateCompactInfoBar_jni.h"

using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;

// Finch parameter names:
const char kTranslateTabDefaultTextColor[] = "translate_tab_default_text_color";

// ChromeTranslateClient
// ----------------------------------------------------------

std::unique_ptr<infobars::InfoBar> ChromeTranslateClient::CreateInfoBar(
    std::unique_ptr<translate::TranslateInfoBarDelegate> delegate) const {
  return std::make_unique<TranslateCompactInfoBar>(std::move(delegate));
}

// TranslateInfoBar -----------------------------------------------------------

TranslateCompactInfoBar::TranslateCompactInfoBar(
    std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
    : infobars::InfoBarAndroid(std::move(delegate)), action_flags_(FLAG_NONE) {
  GetDelegate()->AddObserver(this);

  // Flip the translate bit if auto translate is enabled.
  if (GetDelegate()->translate_step() == translate::TRANSLATE_STEP_TRANSLATING)
    action_flags_ |= FLAG_TRANSLATE;
}

TranslateCompactInfoBar::~TranslateCompactInfoBar() {
  GetDelegate()->RemoveObserver(this);
}

ScopedJavaLocalRef<jobject> TranslateCompactInfoBar::CreateRenderInfoBar(
    JNIEnv* env,
    const ResourceIdMapper& resource_id_mapper) {
  translate::TranslateInfoBarDelegate* delegate = GetDelegate();

  translate::JavaLanguageInfoWrapper translate_languages =
      translate::TranslateUtils::GetTranslateLanguagesInJavaFormat(env,
                                                                   delegate);
  base::android::ScopedJavaLocalRef<jobjectArray> content_languages =
      translate::TranslateUtils::GetContentLanguagesInJavaFormat(env, delegate);
  ScopedJavaLocalRef<jstring> source_language_code =
      base::android::ConvertUTF8ToJavaString(env,
                                             delegate->source_language_code());

  ScopedJavaLocalRef<jstring> target_language_code =
      base::android::ConvertUTF8ToJavaString(env,
                                             delegate->target_language_code());
  content::WebContents* web_contents =
      infobars::ContentInfoBarManager::WebContentsFromInfoBar(this);

  TabAndroid* tab =
      web_contents ? TabAndroid::FromWebContents(web_contents) : nullptr;

  return Java_TranslateCompactInfoBar_create(
      env, tab ? tab->GetJavaObject() : nullptr, delegate->translate_step(),
      source_language_code, target_language_code,
      delegate->ShouldNeverTranslateLanguage(),
      delegate->IsSiteOnNeverPromptList(), delegate->ShouldAlwaysTranslate(),
      delegate->triggered_from_menu(), translate_languages.java_languages,
      translate_languages.java_codes, translate_languages.java_hash_codes,
      content_languages, TabDefaultTextColor());
}

void TranslateCompactInfoBar::ProcessButton(int action) {
  if (!owner())
    return;  // We're closing; don't call anything, it might access the owner.

  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
  if (action == infobars::InfoBarAndroid::ACTION_TRANSLATE) {
    action_flags_ |= FLAG_TRANSLATE;
    delegate->Translate();
    if (delegate->ShouldAutoAlwaysTranslate()) {
      JNIEnv* env = base::android::AttachCurrentThread();
      Java_TranslateCompactInfoBar_setAutoAlwaysTranslate(env,
                                                          GetJavaInfoBar());
    }
    delegate->ReportUIInteraction(translate::UIInteraction::kTranslate);
  } else if (action ==
             infobars::InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) {
    action_flags_ |= FLAG_REVERT;
    delegate->RevertWithoutClosingInfobar();
    delegate->ReportUIInteraction(translate::UIInteraction::kRevert);
  } else {
    DCHECK_EQ(infobars::InfoBarAndroid::ACTION_NONE, action);
  }
}

void TranslateCompactInfoBar::SetJavaInfoBar(
    const base::android::JavaRef<jobject>& java_info_bar) {
  infobars::InfoBarAndroid::SetJavaInfoBar(java_info_bar);
  JNIEnv* env = base::android::AttachCurrentThread();
  Java_TranslateCompactInfoBar_setNativePtr(env, java_info_bar,
                                            reinterpret_cast<intptr_t>(this));
}

void TranslateCompactInfoBar::ApplyStringTranslateOption(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    int option,
    const JavaParamRef<jstring>& value) {
  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
  if (option == translate::TranslateUtils::OPTION_SOURCE_CODE) {
    std::string source_code =
        base::android::ConvertJavaStringToUTF8(env, value);
    delegate->UpdateSourceLanguage(source_code);
    delegate->ReportUIInteraction(
        translate::UIInteraction::kChangeSourceLanguage);
  } else if (option == translate::TranslateUtils::OPTION_TARGET_CODE) {
    std::string target_code =
        base::android::ConvertJavaStringToUTF8(env, value);
    delegate->UpdateTargetLanguage(target_code);
    delegate->ReportUIInteraction(
        translate::UIInteraction::kChangeTargetLanguage);
  } else {
    DCHECK(false);
  }
}

void TranslateCompactInfoBar::ApplyBoolTranslateOption(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    int option,
    jboolean value) {
  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
  if (option == translate::TranslateUtils::OPTION_ALWAYS_TRANSLATE) {
    if (delegate->ShouldAlwaysTranslate() != value) {
      action_flags_ |= FLAG_ALWAYS_TRANSLATE;
      delegate->ToggleAlwaysTranslate();
    }
  } else if (option == translate::TranslateUtils::OPTION_NEVER_TRANSLATE) {
    if (delegate->ShouldNeverTranslateLanguage() != value) {
      action_flags_ |= FLAG_NEVER_LANGUAGE;
      delegate->ToggleTranslatableLanguageByPrefs();
      if (value) {
        RemoveSelf();
        delegate->RevertTranslation();
        delegate->OnInfoBarClosedByUser();
      }
    }
  } else if (option == translate::TranslateUtils::OPTION_NEVER_TRANSLATE_SITE) {
    if (delegate->IsSiteOnNeverPromptList() != value) {
      action_flags_ |= FLAG_NEVER_SITE;
      delegate->ToggleNeverPromptSite();
      if (value) {
        delegate->RevertTranslation();
        RemoveSelf();
        delegate->OnInfoBarClosedByUser();
      }
    }
  } else {
    DCHECK(false);
  }
}

jboolean TranslateCompactInfoBar::ShouldAutoNeverTranslate(
    JNIEnv* env,
    const base::android::JavaParamRef<jobject>& obj,
    jboolean menu_expanded) {
  // Flip menu expanded bit.
  if (menu_expanded)
    action_flags_ |= FLAG_EXPAND_MENU;

  if (!IsDeclinedByUser())
    return false;

  return GetDelegate()->ShouldAutoNeverTranslate();
}

// Returns true if the current tab is an incognito tab.
jboolean TranslateCompactInfoBar::IsIncognito(
    JNIEnv* env,
    const base::android::JavaParamRef<jobject>& obj) {
  content::WebContents* web_contents =
      infobars::ContentInfoBarManager::WebContentsFromInfoBar(this);
  if (!web_contents)
    return false;
  return web_contents->GetBrowserContext()->IsOffTheRecord();
}

base::android::ScopedJavaLocalRef<jobjectArray>
TranslateCompactInfoBar::GetContentLanguagesCodes(
    JNIEnv* env,
    const base::android::JavaParamRef<jobject>& obj) {
  return translate::TranslateUtils::GetContentLanguagesInJavaFormat(
      env, GetDelegate());
}

int TranslateCompactInfoBar::GetParam(const std::string& paramName,
                                      int default_value) {
  std::map<std::string, std::string> params;
  if (!base::GetFieldTrialParams(translate::kTranslateCompactUI.name,
                                 &params)) {
    return default_value;
  }
  int value = 0;
  base::StringToInt(params[paramName], &value);
  return value <= 0 ? default_value : value;
}

int TranslateCompactInfoBar::TabDefaultTextColor() {
  return GetParam(kTranslateTabDefaultTextColor, 0);
}

translate::TranslateInfoBarDelegate* TranslateCompactInfoBar::GetDelegate() {
  return delegate()->AsTranslateInfoBarDelegate();
}

void TranslateCompactInfoBar::OnTranslateStepChanged(
    translate::TranslateStep step,
    translate::TranslateErrors error_type) {
  // TODO(crbug.com/40698822): intended to mitigate a crash where
  // the java infobar is gone. If this works, look into root cause.
  if (!HasSetJavaInfoBar())
    return;  // No connected Java infobar

  if (!owner())
    return;  // We're closing; don't call anything.

  if ((step == translate::TRANSLATE_STEP_AFTER_TRANSLATE) ||
      (step == translate::TRANSLATE_STEP_TRANSLATE_ERROR)) {
    JNIEnv* env = base::android::AttachCurrentThread();
    bool error_ui_shown = Java_TranslateCompactInfoBar_onPageTranslated(
        env, GetJavaInfoBar(), base::to_underlying(error_type));

    if (error_ui_shown) {
      GetDelegate()->OnErrorShown(error_type);
    }
  } else if (step == translate::TRANSLATE_STEP_TRANSLATING) {
    JNIEnv* env = base::android::AttachCurrentThread();
    Java_TranslateCompactInfoBar_onTranslating(env, GetJavaInfoBar());
  }
}

void TranslateCompactInfoBar::OnTargetLanguageChanged(
    const std::string& target_language_code) {
  // TODO(crbug.com/40698822): intended to mitigate a crash where
  // the java infobar is gone. If this works, look into root cause.
  if (!HasSetJavaInfoBar())
    return;  // No connected Java infobar

  if (!owner())
    return;  // We're closing; don't call anything.

  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
  if (delegate->target_language_code().compare(target_language_code) == 0) {
    return;
  }
  delegate->UpdateTargetLanguage(target_language_code);

  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jstring> target_code =
      base::android::ConvertUTF8ToJavaString(env, target_language_code);
  Java_TranslateCompactInfoBar_onTargetLanguageChanged(env, GetJavaInfoBar(),
                                                       target_code);
}

bool TranslateCompactInfoBar::IsDeclinedByUser() {
  // Whether there is any affirmative action bit.
  return action_flags_ == FLAG_NONE;
}

void TranslateCompactInfoBar::OnTranslateInfoBarDelegateDestroyed(
    translate::TranslateInfoBarDelegate* delegate) {
  DCHECK_EQ(GetDelegate(), delegate);
  GetDelegate()->RemoveObserver(this);
}