• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "build/build_config.h"
6 
7 #include "chrome/browser/search_engines/template_url_fetcher.h"
8 
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_process_host.h"
18 #include "content/public/browser/render_view_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"
25 
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);
37 
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;
42 
43   // URL of the OSDD.
url() const44   GURL url() const { return osdd_url_; }
45 
46   // Keyword to use.
keyword() const47   base::string16 keyword() const { return keyword_; }
48 
49   // The type of search provider being fetched.
provider_type() const50   ProviderType provider_type() const { return provider_type_; }
51 
52  private:
53   void OnLoaded();
54   void AddSearchProvider();
55 
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_;
64 
65   scoped_ptr<TemplateURLService::Subscription> template_url_subscription_;
66 
67   DISALLOW_COPY_AND_ASSIGN(RequestDelegate);
68 };
69 
RequestDelegate(TemplateURLFetcher * fetcher,const base::string16 & keyword,const GURL & osdd_url,const GURL & favicon_url,content::WebContents * web_contents,TemplateURLFetcherCallbacks * callbacks,ProviderType provider_type)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.
89 
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();
96   }
97 
98   url_fetcher_->SetRequestContext(fetcher->profile()->GetRequestContext());
99   // Can be NULL during tests.
100   if (web_contents) {
101     content::AssociateURLFetcherWithRenderView(
102         url_fetcher_.get(),
103         web_contents->GetURL(),
104         web_contents->GetRenderProcessHost()->GetID(),
105         web_contents->GetRenderViewHost()->GetRoutingID());
106   }
107 
108   url_fetcher_->Start();
109 }
110 
OnLoaded()111 void TemplateURLFetcher::RequestDelegate::OnLoaded() {
112   template_url_subscription_.reset();
113   if (!template_url_.get())
114     return;
115   AddSearchProvider();
116   // WARNING: AddSearchProvider deletes us.
117 }
118 
OnURLFetchComplete(const net::URLFetcher * source)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;
135   }
136 
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;
143   }
144 
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());
153   }
154 
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.
162 }
163 
AddSearchProvider()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());
171 
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;
180   }
181 
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_;
187 
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_;
191 
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;
198 
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;
209 
210     default:
211       NOTREACHED();
212       break;
213   }
214 
215   fetcher_->RequestCompleted(this);
216   // WARNING: RequestCompleted deletes us.
217 }
218 
219 // TemplateURLFetcher ---------------------------------------------------------
220 
TemplateURLFetcher(Profile * profile)221 TemplateURLFetcher::TemplateURLFetcher(Profile* profile) : profile_(profile) {
222   DCHECK(profile_);
223 }
224 
~TemplateURLFetcher()225 TemplateURLFetcher::~TemplateURLFetcher() {
226 }
227 
ScheduleDownload(const base::string16 & keyword,const GURL & osdd_url,const GURL & favicon_url,content::WebContents * web_contents,TemplateURLFetcherCallbacks * callbacks,ProviderType provider_type)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);
237 
238   TemplateURLService* url_model =
239       TemplateURLServiceFactory::GetForProfile(profile());
240   if (!url_model)
241     return;
242 
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());
248 
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;
254     }
255 
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;
261   }
262 
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;
269   }
270 
271   requests_.push_back(
272       new RequestDelegate(this, keyword, osdd_url, favicon_url, web_contents,
273                           owned_callbacks.release(), provider_type));
274 }
275 
RequestCompleted(RequestDelegate * request)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;
282 }
283