Upload upstream chromium 114.0.5735.31
[platform/framework/web/chromium-efl.git] / components / search_engines / template_url_fetcher.cc
1 // Copyright 2014 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_engines/template_url_fetcher.h"
6
7 #include "base/functional/bind.h"
8 #include "base/memory/raw_ptr.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/ranges/algorithm.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "components/search_engines/template_url.h"
15 #include "components/search_engines/template_url_parser.h"
16 #include "components/search_engines/template_url_service.h"
17 #include "net/base/load_flags.h"
18 #include "net/traffic_annotation/network_traffic_annotation.h"
19 #include "services/network/public/cpp/resource_request.h"
20 #include "services/network/public/cpp/simple_url_loader.h"
21 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
22
23 namespace {
24
25 // In some network environments, silent failure can be avoided by retrying
26 // request on network change. This helps OpenSearch get through in such cases.
27 // See https://crbug.com/956689 for context.
28 constexpr int kOpenSearchRetryCount = 3;
29
30 // Timeout for OpenSearch description document (OSDD) fetch request.
31 // Requests for a particular resource are limited to one.
32 // Requests may not receive a response, and in that case no
33 // further requests would be allowed. The timeout cleans up failed requests
34 // so that later attempts to fetch the OSDD can be made.
35 constexpr int kOpenSearchTimeoutSeconds = 30;
36
37 // Traffic annotation for RequestDelegate.
38 const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
39     net::DefineNetworkTrafficAnnotation("open_search", R"(
40       semantics {
41         sender: "Omnibox"
42         description:
43           "Web pages can include an OpenSearch description doc in their HTML. "
44           "In this case Chromium downloads and parses the file. The "
45           "corresponding search engine is added to the list in the browser "
46           "settings (chrome://settings/searchEngines)."
47         trigger:
48           "User visits a web page containing a <link rel=\"search\"> tag."
49         data: "None"
50         destination: WEBSITE
51       }
52       policy {
53         cookies_allowed: YES
54         cookies_store: "user"
55         setting: "This feature cannot be disabled in settings."
56         policy_exception_justification:
57           "Not implemented, considered not useful as this feature does not "
58           "upload any data."
59       })");
60
61 }  // namespace
62
63 // RequestDelegate ------------------------------------------------------------
64 class TemplateURLFetcher::RequestDelegate {
65  public:
66   RequestDelegate(TemplateURLFetcher* fetcher,
67                   const std::u16string& keyword,
68                   const GURL& osdd_url,
69                   const GURL& favicon_url,
70                   const url::Origin& initiator,
71                   network::mojom::URLLoaderFactory* url_loader_factory,
72                   int render_frame_id,
73                   int32_t request_id);
74
75   RequestDelegate(const RequestDelegate&) = delete;
76   RequestDelegate& operator=(const RequestDelegate&) = delete;
77
78   // If data contains a valid OSDD, a TemplateURL is created and added to
79   // the TemplateURLService.
80   void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
81
82   // URL of the OSDD.
83   GURL url() const { return osdd_url_; }
84
85   // Keyword to use.
86   std::u16string keyword() const { return keyword_; }
87
88  private:
89   void OnTemplateURLParsed(std::unique_ptr<TemplateURL> template_url);
90   void OnLoaded();
91   void AddSearchProvider();
92
93   std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
94   raw_ptr<TemplateURLFetcher> fetcher_;
95   std::unique_ptr<TemplateURL> template_url_;
96   std::u16string keyword_;
97   const GURL osdd_url_;
98   const GURL favicon_url_;
99
100   base::CallbackListSubscription template_url_subscription_;
101
102   base::WeakPtrFactory<RequestDelegate> weak_factory_{this};
103 };
104
105 TemplateURLFetcher::RequestDelegate::RequestDelegate(
106     TemplateURLFetcher* fetcher,
107     const std::u16string& keyword,
108     const GURL& osdd_url,
109     const GURL& favicon_url,
110     const url::Origin& initiator,
111     network::mojom::URLLoaderFactory* url_loader_factory,
112     int render_frame_id,
113     int32_t request_id)
114     : fetcher_(fetcher),
115       keyword_(keyword),
116       osdd_url_(osdd_url),
117       favicon_url_(favicon_url) {
118   TemplateURLService* model = fetcher_->template_url_service_;
119   DCHECK(model);  // TemplateURLFetcher::ScheduleDownload verifies this.
120
121   if (!model->loaded()) {
122     // Start the model load and set-up waiting for it.
123     template_url_subscription_ = model->RegisterOnLoadedCallback(
124         base::BindOnce(&TemplateURLFetcher::RequestDelegate::OnLoaded,
125                        weak_factory_.GetWeakPtr()));
126     model->Load();
127   }
128
129   auto resource_request = std::make_unique<network::ResourceRequest>();
130   resource_request->url = osdd_url;
131   resource_request->request_initiator = initiator;
132   // TODO(crbug.com/1059639): Remove |resource_type| once the request is handled
133   // with RequestDestination without ResourceType.
134   resource_request->resource_type =
135       /* blink::mojom::ResourceType::kSubResource */ 6;
136   resource_request->destination = network::mojom::RequestDestination::kEmpty;
137   resource_request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES;
138   simple_url_loader_ = network::SimpleURLLoader::Create(
139       std::move(resource_request), kTrafficAnnotation);
140   simple_url_loader_->SetAllowHttpErrorResults(true);
141   simple_url_loader_->SetTimeoutDuration(
142       base::Seconds(kOpenSearchTimeoutSeconds));
143   simple_url_loader_->SetRetryOptions(
144       kOpenSearchRetryCount, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
145   simple_url_loader_->SetRequestID(request_id);
146   simple_url_loader_->DownloadToString(
147       url_loader_factory,
148       base::BindOnce(
149           &TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete,
150           weak_factory_.GetWeakPtr()),
151       50000 /* max_body_size */);
152 }
153
154 void TemplateURLFetcher::RequestDelegate::OnTemplateURLParsed(
155     std::unique_ptr<TemplateURL> template_url) {
156   template_url_ = std::move(template_url);
157
158   if (!template_url_ ||
159       !template_url_->url_ref().SupportsReplacement(
160           fetcher_->template_url_service_->search_terms_data())) {
161     fetcher_->RequestCompleted(this);
162     // WARNING: RequestCompleted deletes us.
163     return;
164   }
165
166   // Wait for the model to be loaded before adding the provider.
167   if (!fetcher_->template_url_service_->loaded())
168     return;
169   AddSearchProvider();
170   // WARNING: AddSearchProvider deletes us.
171 }
172
173 void TemplateURLFetcher::RequestDelegate::OnLoaded() {
174   template_url_subscription_ = {};
175   if (!template_url_)
176     return;
177   AddSearchProvider();
178   // WARNING: AddSearchProvider deletes us.
179 }
180
181 void TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete(
182     std::unique_ptr<std::string> response_body) {
183   // Validation checks.
184   // Make sure we can still replace the keyword, i.e. the fetch was successful.
185   if (!response_body) {
186     fetcher_->RequestCompleted(this);
187     // WARNING: RequestCompleted deletes us.
188     return;
189   }
190
191   TemplateURLParser::Parse(
192       &fetcher_->template_url_service_->search_terms_data(),
193       *response_body.get(), TemplateURLParser::ParameterFilter(),
194       base::BindOnce(&RequestDelegate::OnTemplateURLParsed,
195                      weak_factory_.GetWeakPtr()));
196 }
197
198 void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
199   DCHECK(template_url_);
200   DCHECK(!keyword_.empty());
201   TemplateURLService* model = fetcher_->template_url_service_;
202   DCHECK(model);
203   DCHECK(model->loaded());
204
205   if (!model->CanAddAutogeneratedKeyword(keyword_,
206                                          GURL(template_url_->url()))) {
207     fetcher_->RequestCompleted(this);  // WARNING: Deletes us!
208     return;
209   }
210
211   // The short name is what is shown to the user. We preserve original names
212   // since it is better when generated keyword in many cases.
213   TemplateURLData data(template_url_->data());
214   data.SetKeyword(keyword_);
215   data.originating_url = osdd_url_;
216
217   // The page may have specified a URL to use for favicons, if not, set it.
218   if (!data.favicon_url.is_valid())
219     data.favicon_url = favicon_url_;
220
221   // Mark the keyword as replaceable so it can be removed if necessary.
222   // Add() will automatically remove conflicting keyword replaceable engines.
223   data.safe_for_autoreplace = true;
224
225   // Autogenerated keywords are kUnspecified active status by default. When the
226   // active search engine feature flag is enabled, kUnspecified keywords are
227   // inactive and cannot be triggered in the omnibox until they are activated.
228   data.is_active = TemplateURLData::ActiveStatus::kUnspecified;
229
230   model->Add(std::make_unique<TemplateURL>(data));
231
232   fetcher_->RequestCompleted(this);
233   // WARNING: RequestCompleted deletes us.
234 }
235
236 // TemplateURLFetcher ---------------------------------------------------------
237
238 TemplateURLFetcher::TemplateURLFetcher(TemplateURLService* template_url_service)
239     : template_url_service_(template_url_service) {}
240
241 TemplateURLFetcher::~TemplateURLFetcher() {
242 }
243
244 void TemplateURLFetcher::ScheduleDownload(
245     const std::u16string& keyword,
246     const GURL& osdd_url,
247     const GURL& favicon_url,
248     const url::Origin& initiator,
249     network::mojom::URLLoaderFactory* url_loader_factory,
250     int render_frame_id,
251     int32_t request_id) {
252   DCHECK(osdd_url.is_valid());
253   DCHECK(!keyword.empty());
254
255   if (!template_url_service_->loaded()) {
256     // We could try to set up a callback to this function again once the model
257     // is loaded but meh.
258     template_url_service_->Load();
259     return;
260   }
261
262   const TemplateURL* template_url =
263       template_url_service_->GetTemplateURLForKeyword(keyword);
264   if (template_url && (!template_url->safe_for_autoreplace() ||
265                        template_url->originating_url() == osdd_url))
266     return;
267
268   // Make sure we aren't already downloading this request.
269   for (const auto& request : requests_) {
270     if ((request->url() == osdd_url) || (request->keyword() == keyword))
271       return;
272   }
273
274   requests_.push_back(std::make_unique<RequestDelegate>(
275       this, keyword, osdd_url, favicon_url, initiator, url_loader_factory,
276       render_frame_id, request_id));
277 }
278
279 void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
280   auto i = base::ranges::find(requests_, request,
281                               &std::unique_ptr<RequestDelegate>::get);
282   DCHECK(i != requests_.end());
283   requests_.erase(i);
284 }