// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/ui/omnibox/fake_suggestions_database.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
#include "base/json/json_writer.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
namespace {
std::map<std::u16string, std::string> DeserializeJSON(const std::string& str) {
JSONStringValueDeserializer deserializer(str,
base::JSON_ALLOW_TRAILING_COMMAS);
int error_code = 0;
std::string error_message = "";
std::unique_ptr<base::Value> root =
deserializer.Deserialize(&error_code, &error_message);
DCHECK(!error_code) << error_message;
// The root should be a list containing the suggestions.
const base::Value::List& list = root->GetList();
auto fake_suggestions = std::map<std::u16string, std::string>();
for (size_t i = 0; i < list.size(); ++i) {
// A suggest response should be in the list format with the search terms in
// front.
const base::Value::List& response = list[i].GetList();
const std::string& search_terms = response.front().GetString();
std::string serialized_response = "";
JSONStringValueSerializer(&serialized_response).Serialize(response);
fake_suggestions.insert(
std::make_pair(base::UTF8ToUTF16(search_terms), serialized_response));
}
return fake_suggestions;
}
} // namespace
FakeSuggestionsDatabase::FakeSuggestionsDatabase(
TemplateURLService* template_url_service)
: template_url_service_(template_url_service), data_() {}
FakeSuggestionsDatabase::~FakeSuggestionsDatabase() = default;
void FakeSuggestionsDatabase::LoadSuggestionsFromFile(
const base::FilePath& file_path) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&FakeSuggestionsDatabase::LoadFakeSuggestions,
base::Unretained(this), file_path));
}
bool FakeSuggestionsDatabase::HasFakeSuggestions(const GURL& url) const {
std::u16string search_terms = ExtractSearchTerms(url);
return base::Contains(data_, search_terms);
}
std::string FakeSuggestionsDatabase::GetFakeSuggestions(const GURL& url) const {
std::u16string search_terms = ExtractSearchTerms(url);
auto item = data_.find(search_terms);
if (item != data_.end()) {
return item->second;
} else {
return "";
}
}
void FakeSuggestionsDatabase::SetFakeSuggestions(
const GURL& url,
const std::string& fake_suggestions) {
std::u16string search_terms = ExtractSearchTerms(url);
data_[search_terms] = fake_suggestions;
}
std::u16string FakeSuggestionsDatabase::ExtractSearchTerms(
const GURL& url) const {
DCHECK(template_url_service_ != nullptr);
if (!template_url_service_ &&
template_url_service_->GetDefaultSearchProvider()) {
return u"";
}
const TemplateURLRef& suggestion_url_ref =
template_url_service_->GetDefaultSearchProvider()->suggestions_url_ref();
std::u16string search_terms;
url::Parsed::ComponentType search_term_component;
url::Component search_terms_position;
suggestion_url_ref.ExtractSearchTermsFromURL(
url, &search_terms, template_url_service_->search_terms_data(),
&search_term_component, &search_terms_position);
return search_terms;
}
void FakeSuggestionsDatabase::LoadFakeSuggestions(base::FilePath file_path) {
if (!base::PathExists(file_path)) {
NOTREACHED_IN_MIGRATION() << "File doesn't exist";
}
std::string data_str = "";
if (base::ReadFileToString(file_path, &data_str)) {
std::map<std::u16string, std::string> file_data = DeserializeJSON(data_str);
data_.merge(file_data);
}
}