Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / search_engines / template_url_fetcher.cc
blob0d86d682dcd2b06f1f481cd5020eb13d3cae8772
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 {
28 public:
29 // Takes ownership of |callbacks|.
30 RequestDelegate(TemplateURLFetcher* fetcher,
31 const base::string16& keyword,
32 const GURL& osdd_url,
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;
43 // URL of the OSDD.
44 GURL url() const { return osdd_url_; }
46 // Keyword to use.
47 base::string16 keyword() const { return keyword_; }
49 // The type of search provider being fetched.
50 ProviderType provider_type() const { return provider_type_; }
52 private:
53 void OnLoaded();
54 void AddSearchProvider();
56 scoped_ptr<net::URLFetcher> url_fetcher_;
57 TemplateURLFetcher* fetcher_;
58 scoped_ptr<TemplateURL> template_url_;
59 base::string16 keyword_;
60 const GURL osdd_url_;
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,
73 const GURL& osdd_url,
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)),
80 fetcher_(fetcher),
81 keyword_(keyword),
82 osdd_url_(osdd_url),
83 favicon_url_(favicon_url),
84 provider_type_(provider_type),
85 callbacks_(callbacks) {
86 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(
87 fetcher_->profile());
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)));
95 model->Load();
98 url_fetcher_->SetRequestContext(fetcher->profile()->GetRequestContext());
99 // Can be NULL during tests.
100 if (web_contents) {
101 content::AssociateURLFetcherWithRenderFrame(
102 url_fetcher_.get(),
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())
114 return;
115 AddSearchProvider();
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.
127 std::string data;
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.
134 return;
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.
142 return;
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())
159 return;
160 AddSearchProvider();
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);
169 DCHECK(model);
170 DCHECK(model->loaded());
172 TemplateURL* existing_url = NULL;
173 if (model->CanReplaceKeyword(keyword_, GURL(template_url_->url()),
174 &existing_url)) {
175 if (existing_url)
176 model->Remove(existing_url);
177 } else if (provider_type_ == AUTODETECTED_PROVIDER) {
178 fetcher_->RequestCompleted(this); // WARNING: Deletes us!
179 return;
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));
197 break;
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
205 // cancelled.
206 callbacks_->ConfirmAddSearchProvider(new TemplateURL(profile, data),
207 profile);
208 break;
210 default:
211 NOTREACHED();
212 break;
215 fetcher_->RequestCompleted(this);
216 // WARNING: RequestCompleted deletes us.
219 // TemplateURLFetcher ---------------------------------------------------------
221 TemplateURLFetcher::TemplateURLFetcher(Profile* profile) : profile_(profile) {
222 DCHECK(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());
240 if (!url_model)
241 return;
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.
252 url_model->Load();
253 return;
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))
260 return;
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)))
268 return;
271 requests_.push_back(
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);
281 delete request;