1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "build/build_config.h"
7 #include "components/search_engines/template_url_fetcher.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/search_engines/template_url.h"
12 #include "components/search_engines/template_url_parser.h"
13 #include "components/search_engines/template_url_service.h"
14 #include "net/base/load_flags.h"
15 #include "net/url_request/url_fetcher.h"
16 #include "net/url_request/url_fetcher_delegate.h"
17 #include "net/url_request/url_request_context_getter.h"
18 #include "net/url_request/url_request_status.h"
20 // RequestDelegate ------------------------------------------------------------
21 class TemplateURLFetcher::RequestDelegate
: public net::URLFetcherDelegate
{
24 TemplateURLFetcher
* fetcher
,
25 const base::string16
& keyword
,
27 const GURL
& favicon_url
,
28 const URLFetcherCustomizeCallback
& url_fetcher_customize_callback
,
29 const ConfirmAddSearchProviderCallback
& confirm_add_callback
,
30 ProviderType provider_type
);
32 // net::URLFetcherDelegate:
33 // If data contains a valid OSDD, a TemplateURL is created and added to
34 // the TemplateURLService.
35 void OnURLFetchComplete(const net::URLFetcher
* source
) override
;
38 GURL
url() const { return osdd_url_
; }
41 base::string16
keyword() const { return keyword_
; }
43 // The type of search provider being fetched.
44 ProviderType
provider_type() const { return provider_type_
; }
48 void AddSearchProvider();
50 scoped_ptr
<net::URLFetcher
> url_fetcher_
;
51 TemplateURLFetcher
* fetcher_
;
52 scoped_ptr
<TemplateURL
> template_url_
;
53 base::string16 keyword_
;
55 const GURL favicon_url_
;
56 const ProviderType provider_type_
;
57 ConfirmAddSearchProviderCallback confirm_add_callback_
;
59 scoped_ptr
<TemplateURLService::Subscription
> template_url_subscription_
;
61 DISALLOW_COPY_AND_ASSIGN(RequestDelegate
);
64 TemplateURLFetcher::RequestDelegate::RequestDelegate(
65 TemplateURLFetcher
* fetcher
,
66 const base::string16
& keyword
,
68 const GURL
& favicon_url
,
69 const URLFetcherCustomizeCallback
& url_fetcher_customize_callback
,
70 const ConfirmAddSearchProviderCallback
& confirm_add_callback
,
71 ProviderType provider_type
)
72 : url_fetcher_(net::URLFetcher::Create(
73 osdd_url
, net::URLFetcher::GET
, this)),
77 favicon_url_(favicon_url
),
78 provider_type_(provider_type
),
79 confirm_add_callback_(confirm_add_callback
) {
80 TemplateURLService
* model
= fetcher_
->template_url_service_
;
81 DCHECK(model
); // TemplateURLFetcher::ScheduleDownload verifies this.
83 if (!model
->loaded()) {
84 // Start the model load and set-up waiting for it.
85 template_url_subscription_
= model
->RegisterOnLoadedCallback(
86 base::Bind(&TemplateURLFetcher::RequestDelegate::OnLoaded
,
87 base::Unretained(this)));
91 if (!url_fetcher_customize_callback
.is_null())
92 url_fetcher_customize_callback
.Run(url_fetcher_
.get());
94 url_fetcher_
->SetRequestContext(fetcher
->request_context_
.get());
95 url_fetcher_
->Start();
98 void TemplateURLFetcher::RequestDelegate::OnLoaded() {
99 template_url_subscription_
.reset();
100 if (!template_url_
.get())
103 // WARNING: AddSearchProvider deletes us.
106 void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
107 const net::URLFetcher
* source
) {
108 // Validation checks.
109 // Make sure we can still replace the keyword, i.e. the fetch was successful.
110 // If the OSDD file was loaded HTTP, we also have to check the response_code.
111 // For other schemes, e.g. when the OSDD file is bundled with an extension,
112 // the response_code is not applicable and should be -1. Also, ensure that
113 // the returned information results in a valid search URL.
115 if (!source
->GetStatus().is_success() ||
116 ((source
->GetResponseCode() != -1) &&
117 (source
->GetResponseCode() != 200)) ||
118 !source
->GetResponseAsString(&data
)) {
119 fetcher_
->RequestCompleted(this);
120 // WARNING: RequestCompleted deletes us.
124 template_url_
.reset(TemplateURLParser::Parse(
125 fetcher_
->template_url_service_
->search_terms_data(), false,
126 data
.data(), data
.length(), NULL
));
127 if (!template_url_
.get() ||
128 !template_url_
->url_ref().SupportsReplacement(
129 fetcher_
->template_url_service_
->search_terms_data())) {
130 fetcher_
->RequestCompleted(this);
131 // WARNING: RequestCompleted deletes us.
135 if (provider_type_
!= AUTODETECTED_PROVIDER
|| keyword_
.empty()) {
136 // Use the parser-generated new keyword from the URL in the OSDD for the
137 // non-autodetected case. The existing |keyword_| was generated from the
138 // URL that hosted the OSDD, which results in the wrong keyword when the
139 // OSDD was located on a third-party site that has nothing in common with
140 // search engine described by OSDD.
141 keyword_
= template_url_
->keyword();
142 DCHECK(!keyword_
.empty());
145 // Wait for the model to be loaded before adding the provider.
146 if (!fetcher_
->template_url_service_
->loaded())
149 // WARNING: AddSearchProvider deletes us.
152 void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
153 DCHECK(template_url_
.get());
154 DCHECK(!keyword_
.empty());
155 TemplateURLService
* model
= fetcher_
->template_url_service_
;
157 DCHECK(model
->loaded());
159 TemplateURL
* existing_url
= NULL
;
160 if (model
->CanReplaceKeyword(keyword_
, GURL(template_url_
->url()),
163 model
->Remove(existing_url
);
164 } else if (provider_type_
== AUTODETECTED_PROVIDER
) {
165 fetcher_
->RequestCompleted(this); // WARNING: Deletes us!
169 // The short name is what is shown to the user. We preserve original names
170 // since it is better when generated keyword in many cases.
171 TemplateURLData
data(template_url_
->data());
172 data
.SetKeyword(keyword_
);
173 data
.originating_url
= osdd_url_
;
175 // The page may have specified a URL to use for favicons, if not, set it.
176 if (!data
.favicon_url
.is_valid())
177 data
.favicon_url
= favicon_url_
;
179 switch (provider_type_
) {
180 case AUTODETECTED_PROVIDER
:
181 // Mark the keyword as replaceable so it can be removed if necessary.
182 data
.safe_for_autoreplace
= true;
183 model
->Add(new TemplateURL(data
));
186 case EXPLICIT_PROVIDER
:
187 // Confirm addition and allow user to edit default choices. It's ironic
188 // that only *non*-autodetected additions get confirmed, but the user
189 // expects feedback that his action did something.
190 // The source WebContents' delegate takes care of adding the URL to the
191 // model, which takes ownership, or of deleting it if the add is
193 confirm_add_callback_
.Run(make_scoped_ptr(new TemplateURL(data
)));
201 fetcher_
->RequestCompleted(this);
202 // WARNING: RequestCompleted deletes us.
205 // TemplateURLFetcher ---------------------------------------------------------
207 TemplateURLFetcher::TemplateURLFetcher(
208 TemplateURLService
* template_url_service
,
209 net::URLRequestContextGetter
* request_context
)
210 : template_url_service_(template_url_service
),
211 request_context_(request_context
) {
214 TemplateURLFetcher::~TemplateURLFetcher() {
217 void TemplateURLFetcher::ScheduleDownload(
218 const base::string16
& keyword
,
219 const GURL
& osdd_url
,
220 const GURL
& favicon_url
,
221 const URLFetcherCustomizeCallback
& url_fetcher_customize_callback
,
222 const ConfirmAddSearchProviderCallback
& confirm_add_callback
,
223 ProviderType provider_type
) {
224 DCHECK(osdd_url
.is_valid());
226 // For a JS-added OSDD, the provided keyword is irrelevant because we will
227 // generate a keyword later from the OSDD content. For the autodetected case,
228 // we need a valid keyword up front.
229 if (provider_type
== TemplateURLFetcher::AUTODETECTED_PROVIDER
) {
230 DCHECK(!keyword
.empty());
232 if (!template_url_service_
->loaded()) {
233 // We could try to set up a callback to this function again once the model
234 // is loaded but since this is an auto-add case anyway, meh.
235 template_url_service_
->Load();
239 const TemplateURL
* template_url
=
240 template_url_service_
->GetTemplateURLForKeyword(keyword
);
241 if (template_url
&& (!template_url
->safe_for_autoreplace() ||
242 template_url
->originating_url() == osdd_url
))
246 // Make sure we aren't already downloading this request.
247 for (Requests::iterator i
= requests_
.begin(); i
!= requests_
.end(); ++i
) {
248 if (((*i
)->url() == osdd_url
) ||
249 ((provider_type
== TemplateURLFetcher::AUTODETECTED_PROVIDER
) &&
250 ((*i
)->keyword() == keyword
)))
254 requests_
.push_back(new RequestDelegate(
255 this, keyword
, osdd_url
, favicon_url
, url_fetcher_customize_callback
,
256 confirm_add_callback
, provider_type
));
259 void TemplateURLFetcher::RequestCompleted(RequestDelegate
* request
) {
260 Requests::iterator i
=
261 std::find(requests_
.begin(), requests_
.end(), request
);
262 DCHECK(i
!= requests_
.end());
263 requests_
.weak_erase(i
);