1 // Copyright (c) 2012 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 "chrome/browser/search_engines/template_url_fetcher.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/search_engines/template_url.h"
13 #include "chrome/browser/search_engines/template_url_fetcher_callbacks.h"
14 #include "chrome/browser/search_engines/template_url_parser.h"
15 #include "chrome/browser/search_engines/template_url_service.h"
16 #include "chrome/browser/search_engines/template_url_service_factory.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/url_fetcher.h"
21 #include "net/base/load_flags.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_fetcher_delegate.h"
24 #include "net/url_request/url_request_status.h"
26 // RequestDelegate ------------------------------------------------------------
27 class TemplateURLFetcher::RequestDelegate
: public net::URLFetcherDelegate
{
29 // Takes ownership of |callbacks|.
30 RequestDelegate(TemplateURLFetcher
* fetcher
,
31 const base::string16
& keyword
,
33 const GURL
& favicon_url
,
34 content::WebContents
* web_contents
,
35 TemplateURLFetcherCallbacks
* callbacks
,
36 ProviderType provider_type
);
38 // net::URLFetcherDelegate:
39 // If data contains a valid OSDD, a TemplateURL is created and added to
40 // the TemplateURLService.
41 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
;
44 GURL
url() const { return osdd_url_
; }
47 base::string16
keyword() const { return keyword_
; }
49 // The type of search provider being fetched.
50 ProviderType
provider_type() const { return provider_type_
; }
54 void AddSearchProvider();
56 scoped_ptr
<net::URLFetcher
> url_fetcher_
;
57 TemplateURLFetcher
* fetcher_
;
58 scoped_ptr
<TemplateURL
> template_url_
;
59 base::string16 keyword_
;
61 const GURL favicon_url_
;
62 const ProviderType provider_type_
;
63 scoped_ptr
<TemplateURLFetcherCallbacks
> callbacks_
;
65 scoped_ptr
<TemplateURLService::Subscription
> template_url_subscription_
;
67 DISALLOW_COPY_AND_ASSIGN(RequestDelegate
);
70 TemplateURLFetcher::RequestDelegate::RequestDelegate(
71 TemplateURLFetcher
* fetcher
,
72 const base::string16
& keyword
,
74 const GURL
& favicon_url
,
75 content::WebContents
* web_contents
,
76 TemplateURLFetcherCallbacks
* callbacks
,
77 ProviderType provider_type
)
78 : url_fetcher_(net::URLFetcher::Create(
79 osdd_url
, net::URLFetcher::GET
, this)),
83 favicon_url_(favicon_url
),
84 provider_type_(provider_type
),
85 callbacks_(callbacks
) {
86 TemplateURLService
* model
= TemplateURLServiceFactory::GetForProfile(
88 DCHECK(model
); // TemplateURLFetcher::ScheduleDownload verifies this.
90 if (!model
->loaded()) {
91 // Start the model load and set-up waiting for it.
92 template_url_subscription_
= model
->RegisterOnLoadedCallback(
93 base::Bind(&TemplateURLFetcher::RequestDelegate::OnLoaded
,
94 base::Unretained(this)));
98 url_fetcher_
->SetRequestContext(fetcher
->profile()->GetRequestContext());
99 // Can be NULL during tests.
101 content::AssociateURLFetcherWithRenderFrame(
103 web_contents
->GetURL(),
104 web_contents
->GetRenderProcessHost()->GetID(),
105 web_contents
->GetMainFrame()->GetRoutingID());
108 url_fetcher_
->Start();
111 void TemplateURLFetcher::RequestDelegate::OnLoaded() {
112 template_url_subscription_
.reset();
113 if (!template_url_
.get())
116 // WARNING: AddSearchProvider deletes us.
119 void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
120 const net::URLFetcher
* source
) {
121 // Validation checks.
122 // Make sure we can still replace the keyword, i.e. the fetch was successful.
123 // If the OSDD file was loaded HTTP, we also have to check the response_code.
124 // For other schemes, e.g. when the OSDD file is bundled with an extension,
125 // the response_code is not applicable and should be -1. Also, ensure that
126 // the returned information results in a valid search URL.
128 if (!source
->GetStatus().is_success() ||
129 ((source
->GetResponseCode() != -1) &&
130 (source
->GetResponseCode() != 200)) ||
131 !source
->GetResponseAsString(&data
)) {
132 fetcher_
->RequestCompleted(this);
133 // WARNING: RequestCompleted deletes us.
137 template_url_
.reset(TemplateURLParser::Parse(fetcher_
->profile(), false,
138 data
.data(), data
.length(), NULL
));
139 if (!template_url_
.get() || !template_url_
->url_ref().SupportsReplacement()) {
140 fetcher_
->RequestCompleted(this);
141 // WARNING: RequestCompleted deletes us.
145 if (provider_type_
!= AUTODETECTED_PROVIDER
|| keyword_
.empty()) {
146 // Use the parser-generated new keyword from the URL in the OSDD for the
147 // non-autodetected case. The existing |keyword_| was generated from the
148 // URL that hosted the OSDD, which results in the wrong keyword when the
149 // OSDD was located on a third-party site that has nothing in common with
150 // search engine described by OSDD.
151 keyword_
= template_url_
->keyword();
152 DCHECK(!keyword_
.empty());
155 // Wait for the model to be loaded before adding the provider.
156 TemplateURLService
* model
= TemplateURLServiceFactory::GetForProfile(
157 fetcher_
->profile());
158 if (!model
->loaded())
161 // WARNING: AddSearchProvider deletes us.
164 void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
165 DCHECK(template_url_
.get());
166 DCHECK(!keyword_
.empty());
167 Profile
* profile
= fetcher_
->profile();
168 TemplateURLService
* model
= TemplateURLServiceFactory::GetForProfile(profile
);
170 DCHECK(model
->loaded());
172 TemplateURL
* existing_url
= NULL
;
173 if (model
->CanReplaceKeyword(keyword_
, GURL(template_url_
->url()),
176 model
->Remove(existing_url
);
177 } else if (provider_type_
== AUTODETECTED_PROVIDER
) {
178 fetcher_
->RequestCompleted(this); // WARNING: Deletes us!
182 // The short name is what is shown to the user. We preserve original names
183 // since it is better when generated keyword in many cases.
184 TemplateURLData
data(template_url_
->data());
185 data
.SetKeyword(keyword_
);
186 data
.originating_url
= osdd_url_
;
188 // The page may have specified a URL to use for favicons, if not, set it.
189 if (!data
.favicon_url
.is_valid())
190 data
.favicon_url
= favicon_url_
;
192 switch (provider_type_
) {
193 case AUTODETECTED_PROVIDER
:
194 // Mark the keyword as replaceable so it can be removed if necessary.
195 data
.safe_for_autoreplace
= true;
196 model
->Add(new TemplateURL(profile
, data
));
199 case EXPLICIT_PROVIDER
:
200 // Confirm addition and allow user to edit default choices. It's ironic
201 // that only *non*-autodetected additions get confirmed, but the user
202 // expects feedback that his action did something.
203 // The source WebContents' delegate takes care of adding the URL to the
204 // model, which takes ownership, or of deleting it if the add is
206 callbacks_
->ConfirmAddSearchProvider(new TemplateURL(profile
, data
),
215 fetcher_
->RequestCompleted(this);
216 // WARNING: RequestCompleted deletes us.
219 // TemplateURLFetcher ---------------------------------------------------------
221 TemplateURLFetcher::TemplateURLFetcher(Profile
* profile
) : profile_(profile
) {
225 TemplateURLFetcher::~TemplateURLFetcher() {
228 void TemplateURLFetcher::ScheduleDownload(
229 const base::string16
& keyword
,
230 const GURL
& osdd_url
,
231 const GURL
& favicon_url
,
232 content::WebContents
* web_contents
,
233 TemplateURLFetcherCallbacks
* callbacks
,
234 ProviderType provider_type
) {
235 DCHECK(osdd_url
.is_valid());
236 scoped_ptr
<TemplateURLFetcherCallbacks
> owned_callbacks(callbacks
);
238 TemplateURLService
* url_model
=
239 TemplateURLServiceFactory::GetForProfile(profile());
243 // For a JS-added OSDD, the provided keyword is irrelevant because we will
244 // generate a keyword later from the OSDD content. For the autodetected case,
245 // we need a valid keyword up front.
246 if (provider_type
== TemplateURLFetcher::AUTODETECTED_PROVIDER
) {
247 DCHECK(!keyword
.empty());
249 if (!url_model
->loaded()) {
250 // We could try to set up a callback to this function again once the model
251 // is loaded but since this is an auto-add case anyway, meh.
256 const TemplateURL
* template_url
=
257 url_model
->GetTemplateURLForKeyword(keyword
);
258 if (template_url
&& (!template_url
->safe_for_autoreplace() ||
259 template_url
->originating_url() == osdd_url
))
263 // Make sure we aren't already downloading this request.
264 for (Requests::iterator i
= requests_
.begin(); i
!= requests_
.end(); ++i
) {
265 if (((*i
)->url() == osdd_url
) ||
266 ((provider_type
== TemplateURLFetcher::AUTODETECTED_PROVIDER
) &&
267 ((*i
)->keyword() == keyword
)))
272 new RequestDelegate(this, keyword
, osdd_url
, favicon_url
, web_contents
,
273 owned_callbacks
.release(), provider_type
));
276 void TemplateURLFetcher::RequestCompleted(RequestDelegate
* request
) {
277 Requests::iterator i
=
278 std::find(requests_
.begin(), requests_
.end(), request
);
279 DCHECK(i
!= requests_
.end());
280 requests_
.weak_erase(i
);