Fix emulator build error
[platform/framework/web/chromium-efl.git] / components / search / start_suggest_service.cc
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/search/start_suggest_service.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/rand_util.h"
13 #include "base/stl_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/values.h"
16 #include "components/omnibox/browser/autocomplete_scheme_classifier.h"
17 #include "components/omnibox/browser/search_suggestion_parser.h"
18 #include "components/search/search_provider_observer.h"
19 #include "components/search_engines/template_url.h"
20 #include "components/search_engines/template_url_service.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/url_util.h"
23 #include "net/http/http_status_code.h"
24 #include "net/traffic_annotation/network_traffic_annotation.h"
25 #include "services/network/public/cpp/resource_request.h"
26 #include "services/network/public/cpp/shared_url_loader_factory.h"
27 #include "services/network/public/cpp/simple_url_loader.h"
28
29 namespace {
30 // A cache of trending query suggestions using JSON serialized into a string.
31 const char kTrendingQuerySuggestionCachedResults[] =
32     "TrendingQuerySuggestionCachedResults";
33
34 const char kXSSIResponsePreamble[] = ")]}'";
35 }  // namespace
36
37 StartSuggestService::StartSuggestService(
38     TemplateURLService* template_url_service,
39     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
40     std::unique_ptr<AutocompleteSchemeClassifier> scheme_classifier,
41     const std::string& application_country,
42     const std::string& application_locale,
43     const GURL& request_initiator_url)
44     : template_url_service_(template_url_service),
45       url_loader_factory_(url_loader_factory),
46       scheme_classifier_(std::move(scheme_classifier)),
47       application_country_(application_country),
48       application_locale_(application_locale),
49       request_initiator_url_(request_initiator_url),
50       search_provider_observer_(std::make_unique<SearchProviderObserver>(
51           template_url_service_,
52           base::BindRepeating(&StartSuggestService::SearchProviderChanged,
53                               base::Unretained(this)))) {
54   DCHECK(template_url_service);
55   DCHECK(url_loader_factory_);
56   DCHECK(scheme_classifier_);
57 }
58
59 StartSuggestService::~StartSuggestService() = default;
60
61 void StartSuggestService::FetchSuggestions(
62     const TemplateURLRef::SearchTermsArgs& args,
63     SuggestResultCallback callback,
64     bool fetch_from_server) {
65   // Do nothing if Google not default search engine.
66   if (!search_provider_observer()->is_google()) {
67     std::move(callback).Run(QuerySuggestions());
68     return;
69   }
70
71   // If there are saved suggestions from a previous request, return that.
72   if (!fetch_from_server &&
73       suggestions_cache_.find(kTrendingQuerySuggestionCachedResults) !=
74           suggestions_cache_.end()) {
75     QuerySuggestions cache =
76         suggestions_cache_[kTrendingQuerySuggestionCachedResults];
77     if (!cache.empty()) {
78       std::move(callback).Run(std::move(cache));
79       return;
80     }
81   }
82
83   net::NetworkTrafficAnnotationTag traffic_annotation =
84       net::DefineNetworkTrafficAnnotation("chrome_search_suggest_service",
85                                           R"(
86         semantics {
87           sender: "Chrome Search Suggest Service"
88           description:
89             "Fetch query suggestions to be shown in NTP."
90           trigger:
91             "Displaying on the new tab page, if Google is the "
92             "configured search provider."
93           data: "None"
94           destination: GOOGLE_OWNED_SERVICE
95         }
96         policy {
97           cookies_allowed: NO
98           setting:
99             "Users can control this feature by selecting a non-Google default "
100             "search engine in Chrome settings under 'Search Engine'"
101           chrome_policy {
102             DefaultSearchProviderEnabled {
103               policy_options {mode: MANDATORY}
104               DefaultSearchProviderEnabled: false
105             }
106           }
107         })");
108
109   auto resource_request = std::make_unique<network::ResourceRequest>();
110   const GURL& request_url = GetRequestURL(args);
111
112   resource_request->url = request_url;
113   // Do not send credentials since Trending Queries is locale-based.
114   resource_request->credentials_mode =
115       network::mojom::CredentialsMode::kOmitBug_775438_Workaround;
116   resource_request->request_initiator =
117       url::Origin::Create(request_initiator_url_);
118
119   loaders_.push_back(network::SimpleURLLoader::Create(
120       std::move(resource_request), traffic_annotation));
121   loaders_.back()->DownloadToString(
122       url_loader_factory_.get(),
123       base::BindOnce(&StartSuggestService::SuggestResponseLoaded,
124                      weak_ptr_factory_.GetWeakPtr(), loaders_.back().get(),
125                      std::move(callback)),
126       network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
127 }
128
129 void StartSuggestService::SearchProviderChanged() {
130   // Remove cached results if the default search engine changes.
131   suggestions_cache_.clear();
132 }
133
134 GURL StartSuggestService::GetRequestURL(
135     const TemplateURLRef::SearchTermsArgs& search_terms_args) {
136   const TemplateURL* default_provider =
137       template_url_service_->GetDefaultSearchProvider();
138   DCHECK(default_provider);
139   const TemplateURLRef& suggestion_url_ref =
140       default_provider->suggestions_url_ref();
141   const SearchTermsData& search_terms_data =
142       template_url_service_->search_terms_data();
143   DCHECK(suggestion_url_ref.SupportsReplacement(search_terms_data));
144   GURL url = GURL(suggestion_url_ref.ReplaceSearchTerms(search_terms_args,
145                                                         search_terms_data));
146   if (!application_country_.empty()) {
147     // Trending Queries are country-based, so passing this helps determine
148     // locale.
149     url = net::AppendQueryParameter(url, "gl", application_country_);
150   }
151   if (!application_locale_.empty()) {
152     // Language is also used in addition to country to rank suggestions,
153     // ensuring that there can be separate ranks for different languages in the
154     // same country (i.e. fr-ca and en-ca.
155     url = net::AppendQueryParameter(url, "hl", application_locale_);
156   }
157   return url;
158 }
159
160 GURL StartSuggestService::GetQueryDestinationURL(
161     const std::u16string& query,
162     const TemplateURL* search_provider) {
163   TemplateURLRef::SearchTermsArgs search_terms_args(query);
164   DCHECK(search_provider);
165   const TemplateURLRef& search_url_ref = search_provider->url_ref();
166   const SearchTermsData& search_terms_data =
167       template_url_service_->search_terms_data();
168   DCHECK(search_url_ref.SupportsReplacement(search_terms_data));
169   return GURL(
170       search_url_ref.ReplaceSearchTerms(search_terms_args, search_terms_data));
171 }
172
173 SearchProviderObserver* StartSuggestService::search_provider_observer() {
174   return search_provider_observer_.get();
175 }
176
177 void StartSuggestService::SuggestResponseLoaded(
178     network::SimpleURLLoader* loader,
179     SuggestResultCallback callback,
180     std::unique_ptr<std::string> response) {
181   // Ensure the request succeeded and that the provider used is still available.
182   // A verbatim match cannot be generated without this provider, causing errors.
183   const bool request_succeeded = response && loader->NetError() == net::OK;
184   base::EraseIf(loaders_, [loader](const auto& loader_ptr) {
185     return loader == loader_ptr.get();
186   });
187   if (!request_succeeded) {
188     std::move(callback).Run(QuerySuggestions());
189     return;
190   }
191
192   if (base::StartsWith(*response, kXSSIResponsePreamble,
193                        base::CompareCase::SENSITIVE)) {
194     *response = response->substr(strlen(kXSSIResponsePreamble));
195   }
196
197   data_decoder::DataDecoder::ParseJsonIsolated(
198       *response,
199       base::BindOnce(&StartSuggestService::SuggestionsParsed,
200                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
201 }
202
203 void StartSuggestService::SuggestionsParsed(
204     SuggestResultCallback callback,
205     data_decoder::DataDecoder::ValueOrError result) {
206   std::move(callback).Run([&] {
207     QuerySuggestions query_suggestions;
208     if (result.has_value() && result.value().is_list()) {
209       SearchSuggestionParser::Results results;
210       AutocompleteInput input;
211       if (SearchSuggestionParser::ParseSuggestResults(
212               result->GetList(), input, *scheme_classifier_,
213               /*default_result_relevance=*/-1, /*is_keyword_result=*/false,
214               &results)) {
215         for (SearchSuggestionParser::SuggestResult suggest :
216              results.suggest_results) {
217           QuerySuggestion query;
218           query.query = suggest.suggestion();
219           query.destination_url = GetQueryDestinationURL(
220               query.query, template_url_service_->GetDefaultSearchProvider());
221           query_suggestions.push_back(std::move(query));
222         }
223         suggestions_cache_[kTrendingQuerySuggestionCachedResults] =
224             query_suggestions;
225       }
226     }
227     return query_suggestions;
228   }());
229 }