Home | History | Annotate | Download | only in search_engines
      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/chrome_notification_types.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/search_engines/template_url.h"
     14 #include "chrome/browser/search_engines/template_url_fetcher_callbacks.h"
     15 #include "chrome/browser/search_engines/template_url_parser.h"
     16 #include "chrome/browser/search_engines/template_url_service.h"
     17 #include "chrome/browser/search_engines/template_url_service_factory.h"
     18 #include "content/public/browser/notification_observer.h"
     19 #include "content/public/browser/notification_registrar.h"
     20 #include "content/public/browser/notification_source.h"
     21 #include "content/public/browser/render_process_host.h"
     22 #include "content/public/browser/render_view_host.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/common/url_fetcher.h"
     25 #include "net/base/load_flags.h"
     26 #include "net/url_request/url_fetcher.h"
     27 #include "net/url_request/url_fetcher_delegate.h"
     28 #include "net/url_request/url_request_status.h"
     29 
     30 // RequestDelegate ------------------------------------------------------------
     31 class TemplateURLFetcher::RequestDelegate
     32     : public net::URLFetcherDelegate,
     33       public content::NotificationObserver {
     34  public:
     35   // Takes ownership of |callbacks|.
     36   RequestDelegate(TemplateURLFetcher* fetcher,
     37                   const string16& keyword,
     38                   const GURL& osdd_url,
     39                   const GURL& favicon_url,
     40                   content::WebContents* web_contents,
     41                   TemplateURLFetcherCallbacks* callbacks,
     42                   ProviderType provider_type);
     43 
     44   // content::NotificationObserver:
     45   virtual void Observe(int type,
     46                        const content::NotificationSource& source,
     47                        const content::NotificationDetails& details) OVERRIDE;
     48 
     49   // net::URLFetcherDelegate:
     50   // If data contains a valid OSDD, a TemplateURL is created and added to
     51   // the TemplateURLService.
     52   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
     53 
     54   // URL of the OSDD.
     55   GURL url() const { return osdd_url_; }
     56 
     57   // Keyword to use.
     58   string16 keyword() const { return keyword_; }
     59 
     60   // The type of search provider being fetched.
     61   ProviderType provider_type() const { return provider_type_; }
     62 
     63  private:
     64   void AddSearchProvider();
     65 
     66   scoped_ptr<net::URLFetcher> url_fetcher_;
     67   TemplateURLFetcher* fetcher_;
     68   scoped_ptr<TemplateURL> template_url_;
     69   string16 keyword_;
     70   const GURL osdd_url_;
     71   const GURL favicon_url_;
     72   const ProviderType provider_type_;
     73   scoped_ptr<TemplateURLFetcherCallbacks> callbacks_;
     74 
     75   // Handles registering for our notifications.
     76   content::NotificationRegistrar registrar_;
     77 
     78   DISALLOW_COPY_AND_ASSIGN(RequestDelegate);
     79 };
     80 
     81 TemplateURLFetcher::RequestDelegate::RequestDelegate(
     82     TemplateURLFetcher* fetcher,
     83     const string16& keyword,
     84     const GURL& osdd_url,
     85     const GURL& favicon_url,
     86     content::WebContents* web_contents,
     87     TemplateURLFetcherCallbacks* callbacks,
     88     ProviderType provider_type)
     89     : url_fetcher_(net::URLFetcher::Create(
     90           osdd_url, net::URLFetcher::GET, this)),
     91       fetcher_(fetcher),
     92       keyword_(keyword),
     93       osdd_url_(osdd_url),
     94       favicon_url_(favicon_url),
     95       provider_type_(provider_type),
     96       callbacks_(callbacks) {
     97   TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(
     98       fetcher_->profile());
     99   DCHECK(model);  // TemplateURLFetcher::ScheduleDownload verifies this.
    100 
    101   if (!model->loaded()) {
    102     // Start the model load and set-up waiting for it.
    103     registrar_.Add(this,
    104                    chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
    105                    content::Source<TemplateURLService>(model));
    106     model->Load();
    107   }
    108 
    109   url_fetcher_->SetRequestContext(fetcher->profile()->GetRequestContext());
    110   // Can be NULL during tests.
    111   if (web_contents) {
    112     content::AssociateURLFetcherWithRenderView(
    113         url_fetcher_.get(),
    114         web_contents->GetURL(),
    115         web_contents->GetRenderProcessHost()->GetID(),
    116         web_contents->GetRenderViewHost()->GetRoutingID());
    117   }
    118 
    119   url_fetcher_->Start();
    120 }
    121 
    122 void TemplateURLFetcher::RequestDelegate::Observe(
    123     int type,
    124     const content::NotificationSource& source,
    125     const content::NotificationDetails& details) {
    126   DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED);
    127 
    128   if (!template_url_.get())
    129     return;
    130   AddSearchProvider();
    131   // WARNING: AddSearchProvider deletes us.
    132 }
    133 
    134 void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
    135     const net::URLFetcher* source) {
    136   // Validation checks.
    137   // Make sure we can still replace the keyword, i.e. the fetch was successful.
    138   // If the OSDD file was loaded HTTP, we also have to check the response_code.
    139   // For other schemes, e.g. when the OSDD file is bundled with an extension,
    140   // the response_code is not applicable and should be -1. Also, ensure that
    141   // the returned information results in a valid search URL.
    142   std::string data;
    143   if (!source->GetStatus().is_success() ||
    144       ((source->GetResponseCode() != -1) &&
    145         (source->GetResponseCode() != 200)) ||
    146       !source->GetResponseAsString(&data)) {
    147     fetcher_->RequestCompleted(this);
    148     // WARNING: RequestCompleted deletes us.
    149     return;
    150   }
    151 
    152   template_url_.reset(TemplateURLParser::Parse(fetcher_->profile(), false,
    153       data.data(), data.length(), NULL));
    154   if (!template_url_.get() || !template_url_->url_ref().SupportsReplacement()) {
    155     fetcher_->RequestCompleted(this);
    156     // WARNING: RequestCompleted deletes us.
    157     return;
    158   }
    159 
    160   if (provider_type_ != AUTODETECTED_PROVIDER || keyword_.empty()) {
    161     // Use the parser-generated new keyword from the URL in the OSDD for the
    162     // non-autodetected case.  The existing |keyword_| was generated from the
    163     // URL that hosted the OSDD, which results in the wrong keyword when the
    164     // OSDD was located on a third-party site that has nothing in common with
    165     // search engine described by OSDD.
    166     keyword_ = template_url_->keyword();
    167     DCHECK(!keyword_.empty());
    168   }
    169 
    170   // Wait for the model to be loaded before adding the provider.
    171   TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(
    172       fetcher_->profile());
    173   if (!model->loaded())
    174     return;
    175   AddSearchProvider();
    176   // WARNING: AddSearchProvider deletes us.
    177 }
    178 
    179 void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
    180   DCHECK(template_url_.get());
    181   DCHECK(!keyword_.empty());
    182   Profile* profile = fetcher_->profile();
    183   TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile);
    184   DCHECK(model);
    185   DCHECK(model->loaded());
    186 
    187   TemplateURL* existing_url = NULL;
    188   if (model->CanReplaceKeyword(keyword_, GURL(template_url_->url()),
    189                                &existing_url)) {
    190     if (existing_url)
    191       model->Remove(existing_url);
    192   } else if (provider_type_ == AUTODETECTED_PROVIDER) {
    193     fetcher_->RequestCompleted(this);  // WARNING: Deletes us!
    194     return;
    195   }
    196 
    197   // The short name is what is shown to the user. We preserve original names
    198   // since it is better when generated keyword in many cases.
    199   TemplateURLData data(template_url_->data());
    200   data.SetKeyword(keyword_);
    201   data.originating_url = osdd_url_;
    202 
    203   // The page may have specified a URL to use for favicons, if not, set it.
    204   if (!data.favicon_url.is_valid())
    205     data.favicon_url = favicon_url_;
    206 
    207   switch (provider_type_) {
    208     case AUTODETECTED_PROVIDER:
    209       // Mark the keyword as replaceable so it can be removed if necessary.
    210       data.safe_for_autoreplace = true;
    211       model->Add(new TemplateURL(profile, data));
    212       break;
    213 
    214     case EXPLICIT_PROVIDER:
    215       // Confirm addition and allow user to edit default choices. It's ironic
    216       // that only *non*-autodetected additions get confirmed, but the user
    217       // expects feedback that his action did something.
    218       // The source WebContents' delegate takes care of adding the URL to the
    219       // model, which takes ownership, or of deleting it if the add is
    220       // cancelled.
    221       callbacks_->ConfirmAddSearchProvider(new TemplateURL(profile, data),
    222                                            profile);
    223       break;
    224 
    225     default:
    226       NOTREACHED();
    227       break;
    228   }
    229 
    230   fetcher_->RequestCompleted(this);
    231   // WARNING: RequestCompleted deletes us.
    232 }
    233 
    234 // TemplateURLFetcher ---------------------------------------------------------
    235 
    236 TemplateURLFetcher::TemplateURLFetcher(Profile* profile) : profile_(profile) {
    237   DCHECK(profile_);
    238 }
    239 
    240 TemplateURLFetcher::~TemplateURLFetcher() {
    241 }
    242 
    243 void TemplateURLFetcher::ScheduleDownload(
    244     const string16& keyword,
    245     const GURL& osdd_url,
    246     const GURL& favicon_url,
    247     content::WebContents* web_contents,
    248     TemplateURLFetcherCallbacks* callbacks,
    249     ProviderType provider_type) {
    250   DCHECK(osdd_url.is_valid());
    251   scoped_ptr<TemplateURLFetcherCallbacks> owned_callbacks(callbacks);
    252 
    253   TemplateURLService* url_model =
    254       TemplateURLServiceFactory::GetForProfile(profile());
    255   if (!url_model)
    256     return;
    257 
    258   // For a JS-added OSDD, the provided keyword is irrelevant because we will
    259   // generate a keyword later from the OSDD content.  For the autodetected case,
    260   // we need a valid keyword up front.
    261   if (provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER) {
    262     DCHECK(!keyword.empty());
    263 
    264     if (!url_model->loaded()) {
    265       // We could try to set up a callback to this function again once the model
    266       // is loaded but since this is an auto-add case anyway, meh.
    267       url_model->Load();
    268       return;
    269     }
    270 
    271     const TemplateURL* template_url =
    272         url_model->GetTemplateURLForKeyword(keyword);
    273     if (template_url && (!template_url->safe_for_autoreplace() ||
    274                          template_url->originating_url() == osdd_url))
    275       return;
    276   }
    277 
    278   // Make sure we aren't already downloading this request.
    279   for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) {
    280     if (((*i)->url() == osdd_url) ||
    281         ((provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER) &&
    282          ((*i)->keyword() == keyword)))
    283       return;
    284   }
    285 
    286   requests_.push_back(
    287       new RequestDelegate(this, keyword, osdd_url, favicon_url, web_contents,
    288                           owned_callbacks.release(), provider_type));
    289 }
    290 
    291 void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
    292   Requests::iterator i =
    293       std::find(requests_.begin(), requests_.end(), request);
    294   DCHECK(i != requests_.end());
    295   requests_.weak_erase(i);
    296   delete request;
    297 }
    298