chromium/chrome/browser/android/locale/locale_template_url_loader.cc

// Copyright 2018 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/android/locale/locale_template_url_loader.h"

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
#include "base/check_deref.h"
#include "base/debug/dump_without_crashing.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "components/search_engines/prepopulated_engines.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_prepopulate_data.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/util.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/browser/locale/jni_headers/LocaleTemplateUrlLoader_jni.h"

using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;

static jlong JNI_LocaleTemplateUrlLoader_Init(
    JNIEnv* env,
    const JavaParamRef<jstring>& jlocale,
    Profile* profile) {
  return reinterpret_cast<intptr_t>(new LocaleTemplateUrlLoader(
      ConvertJavaStringToUTF8(env, jlocale),
      TemplateURLServiceFactory::GetForProfile(profile), profile));
}

LocaleTemplateUrlLoader::LocaleTemplateUrlLoader(const std::string& locale,
                                                 TemplateURLService* service,
                                                 Profile* profile)
    : locale_(locale), template_url_service_(service) {
  profile_observation_.Observe(profile);
}

void LocaleTemplateUrlLoader::Destroy(JNIEnv* env) {
  delete this;
}

void LocaleTemplateUrlLoader::OnProfileWillBeDestroyed(Profile* profile) {
  // There is a risk that java keeps a reference to this loader and attempts to
  // use it even if we started destroying the profile on the native side. To
  // protect against this we remove access to the `template_url_service_` and
  // stub out subsequent the calls.
  profile_observation_.Reset();
  template_url_service_ = nullptr;
}

jboolean LocaleTemplateUrlLoader::LoadTemplateUrls(JNIEnv* env) {
  DCHECK(locale_.length() == 2);

  if (!template_url_service_) {
    // TODO(b/318339172): Test profile state from Java, switch to CHECK here.
    base::debug::DumpWithoutCrashing();  // Investigating b/317335096.
    return false;
  }

  std::vector<std::unique_ptr<TemplateURLData>> prepopulated_list =
      GetLocalPrepopulatedEngines();

  if (prepopulated_list.empty()) {
    return false;
  }

  for (const auto& data_url : prepopulated_list) {
    // Attempt to see if the URL already exists in the list of template URLs.
    //
    // Special case Google because the keyword is mutated based on the results
    // of the GoogleUrlTracker, so we need to rely on prepopulate ID instead
    // of keyword only for Google.
    //
    // Otherwise, matching based on keyword is sufficient and preferred as
    // some logically distinct search engines share the same prepopulate ID and
    // only differ on keyword.
    const TemplateURL* matching_url =
        template_url_service_->GetTemplateURLForKeyword(data_url->keyword());
    bool exists = matching_url != nullptr;
    if (!exists &&
        data_url->prepopulate_id == TemplateURLPrepopulateData::google.id) {
      auto existing_urls = template_url_service_->GetTemplateURLs();

      for (TemplateURL* existing_url : existing_urls) {
        if (existing_url->prepopulate_id() ==
            TemplateURLPrepopulateData::google.id) {
          matching_url = existing_url;
          exists = true;
          break;
        }
      }
    }

    if (exists) {
      continue;
    }

    data_url.get()->safe_for_autoreplace = true;
    std::unique_ptr<TemplateURL> turl(
        new TemplateURL(*data_url, TemplateURL::LOCAL));
    TemplateURL* added_turl = template_url_service_->Add(std::move(turl));
    if (added_turl) {
      prepopulate_ids_.push_back(added_turl->prepopulate_id());
    }
  }
  return true;
}

void LocaleTemplateUrlLoader::RemoveTemplateUrls(JNIEnv* env) {
  if (!template_url_service_) {
    // TODO(b/318339172): Test profile state from Java, switch to CHECK here.
    base::debug::DumpWithoutCrashing();  // Investigating b/317335096.
    return;
  }

  while (!prepopulate_ids_.empty()) {
    TemplateURL* turl = FindURLByPrepopulateID(
        template_url_service_->GetTemplateURLs(), prepopulate_ids_.back());
    if (turl && template_url_service_->GetDefaultSearchProvider() != turl) {
      template_url_service_->Remove(turl);
    }
    prepopulate_ids_.pop_back();
  }
}

void LocaleTemplateUrlLoader::OverrideDefaultSearchProvider(JNIEnv* env) {
  if (!template_url_service_) {
    // TODO(b/318339172): Test profile state from Java, switch to CHECK here.
    base::debug::DumpWithoutCrashing();  // Investigating b/317335096.
    return;
  }

  // If the user has changed their default search provider, no-op.
  const TemplateURL* current_dsp =
      template_url_service_->GetDefaultSearchProvider();
  if (!current_dsp ||
      current_dsp->prepopulate_id() != TemplateURLPrepopulateData::google.id) {
    return;
  }

  TemplateURL* turl =
      FindURLByPrepopulateID(template_url_service_->GetTemplateURLs(),
                             GetDesignatedSearchEngineForChina());
  if (turl) {
    template_url_service_->SetUserSelectedDefaultSearchProvider(turl);
  }
}

void LocaleTemplateUrlLoader::SetGoogleAsDefaultSearch(JNIEnv* env) {
  if (!template_url_service_) {
    // TODO(b/318339172): Test profile state from Java, switch to CHECK here.
    base::debug::DumpWithoutCrashing();  // Investigating b/317335096.
    return;
  }

  // If the user has changed their default search provider, no-op.
  const TemplateURL* current_dsp =
      template_url_service_->GetDefaultSearchProvider();
  if (!current_dsp ||
      current_dsp->prepopulate_id() != GetDesignatedSearchEngineForChina()) {
    return;
  }

  TemplateURL* turl =
      FindURLByPrepopulateID(template_url_service_->GetTemplateURLs(),
                             TemplateURLPrepopulateData::google.id);
  if (turl) {
    template_url_service_->SetUserSelectedDefaultSearchProvider(turl);
  }
}

std::vector<std::unique_ptr<TemplateURLData>>
LocaleTemplateUrlLoader::GetLocalPrepopulatedEngines() {
  if (!template_url_service_) {
    // TODO(b/318339172): Test profile state from Java, switch to CHECK here.
    base::debug::DumpWithoutCrashing();  // Investigating b/317335096.
    return std::vector<std::unique_ptr<TemplateURLData>>();
  }

  return template_url_service_->GetTemplateURLsForCountry(locale_);
}

int LocaleTemplateUrlLoader::GetDesignatedSearchEngineForChina() {
  return TemplateURLPrepopulateData::sogou.id;
}

LocaleTemplateUrlLoader::~LocaleTemplateUrlLoader() {}