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 "chrome/browser/search_engines/search_provider_install_data.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/sequenced_task_runner_helpers.h"
     17 #include "components/google/core/browser/google_url_tracker.h"
     18 #include "components/search_engines/search_host_to_urls_map.h"
     19 #include "components/search_engines/search_terms_data.h"
     20 #include "components/search_engines/template_url.h"
     21 #include "components/search_engines/template_url_service.h"
     22 #include "components/search_engines/util.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/browser/render_process_host.h"
     25 #include "content/public/browser/render_process_host_observer.h"
     26 
     27 using content::BrowserThread;
     28 
     29 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
     30 
     31 namespace {
     32 
     33 void LoadDataOnUIThread(TemplateURLService* template_url_service,
     34                         const base::Callback<void(ScopedVector<TemplateURL>,
     35                                                   TemplateURL*)>& callback) {
     36   ScopedVector<TemplateURL> template_url_copies;
     37   TemplateURL* default_provider_copy = NULL;
     38   TemplateURLService::TemplateURLVector original_template_urls =
     39       template_url_service->GetTemplateURLs();
     40   TemplateURL* original_default_provider =
     41       template_url_service->GetDefaultSearchProvider();
     42   for (TemplateURLService::TemplateURLVector::const_iterator it =
     43            original_template_urls.begin();
     44        it != original_template_urls.end();
     45        ++it) {
     46     template_url_copies.push_back(new TemplateURL((*it)->data()));
     47     if (*it == original_default_provider)
     48       default_provider_copy = template_url_copies.back();
     49   }
     50   BrowserThread::PostTask(BrowserThread::IO,
     51                           FROM_HERE,
     52                           base::Bind(callback,
     53                                      base::Passed(template_url_copies.Pass()),
     54                                      base::Unretained(default_provider_copy)));
     55 }
     56 
     57 // Implementation of SearchTermsData that may be used on the I/O thread.
     58 class IOThreadSearchTermsData : public SearchTermsData {
     59  public:
     60   explicit IOThreadSearchTermsData(const std::string& google_base_url);
     61 
     62   // Implementation of SearchTermsData.
     63   virtual std::string GoogleBaseURLValue() const OVERRIDE;
     64 
     65  private:
     66   std::string google_base_url_;
     67 
     68   DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData);
     69 };
     70 
     71 IOThreadSearchTermsData::IOThreadSearchTermsData(
     72     const std::string& google_base_url) : google_base_url_(google_base_url) {
     73 }
     74 
     75 std::string IOThreadSearchTermsData::GoogleBaseURLValue() const {
     76   return google_base_url_;
     77 }
     78 
     79 // Handles telling SearchProviderInstallData about changes to the google base
     80 // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is
     81 // deleted on the correct thread.)
     82 class GoogleURLChangeNotifier
     83     : public base::RefCountedThreadSafe<GoogleURLChangeNotifier,
     84                                         BrowserThread::DeleteOnIOThread> {
     85  public:
     86   explicit GoogleURLChangeNotifier(
     87       const base::WeakPtr<SearchProviderInstallData>& install_data);
     88 
     89   // Called on the I/O thread with the Google base URL whenever the value
     90   // changes.
     91   void OnChange(const std::string& google_base_url);
     92 
     93  private:
     94   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
     95   friend class base::DeleteHelper<GoogleURLChangeNotifier>;
     96 
     97   ~GoogleURLChangeNotifier() {}
     98 
     99   base::WeakPtr<SearchProviderInstallData> install_data_;
    100 
    101   DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier);
    102 };
    103 
    104 GoogleURLChangeNotifier::GoogleURLChangeNotifier(
    105     const base::WeakPtr<SearchProviderInstallData>& install_data)
    106     : install_data_(install_data) {
    107 }
    108 
    109 void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) {
    110   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    111   if (install_data_.get())
    112     install_data_->OnGoogleURLChange(google_base_url);
    113 }
    114 
    115 // Notices changes in the Google base URL and sends them along
    116 // to the SearchProviderInstallData on the I/O thread.
    117 class GoogleURLObserver : public content::RenderProcessHostObserver {
    118  public:
    119   GoogleURLObserver(GoogleURLTracker* google_url_tracker,
    120                     GoogleURLChangeNotifier* change_notifier,
    121                     content::RenderProcessHost* host);
    122 
    123   // Implementation of content::RenderProcessHostObserver.
    124   virtual void RenderProcessHostDestroyed(
    125         content::RenderProcessHost* host) OVERRIDE;
    126 
    127  private:
    128   virtual ~GoogleURLObserver() {}
    129 
    130   // Callback that is called when the Google URL is updated.
    131   void OnGoogleURLUpdated();
    132 
    133   GoogleURLTracker* google_url_tracker_;
    134   scoped_refptr<GoogleURLChangeNotifier> change_notifier_;
    135 
    136   scoped_ptr<GoogleURLTracker::Subscription> google_url_updated_subscription_;
    137 
    138   DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver);
    139 };
    140 
    141 GoogleURLObserver::GoogleURLObserver(
    142     GoogleURLTracker* google_url_tracker,
    143     GoogleURLChangeNotifier* change_notifier,
    144     content::RenderProcessHost* host)
    145     : google_url_tracker_(google_url_tracker),
    146       change_notifier_(change_notifier) {
    147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    148   google_url_updated_subscription_ =
    149       google_url_tracker_->RegisterCallback(base::Bind(
    150           &GoogleURLObserver::OnGoogleURLUpdated, base::Unretained(this)));
    151   host->AddObserver(this);
    152 }
    153 
    154 void GoogleURLObserver::OnGoogleURLUpdated() {
    155   BrowserThread::PostTask(BrowserThread::IO,
    156                           FROM_HERE,
    157                           base::Bind(&GoogleURLChangeNotifier::OnChange,
    158                                      change_notifier_.get(),
    159                                      google_url_tracker_->google_url().spec()));
    160 }
    161 
    162 void GoogleURLObserver::RenderProcessHostDestroyed(
    163     content::RenderProcessHost* host) {
    164   delete this;
    165 }
    166 
    167 // Indicates if the two inputs have the same security origin.
    168 // |requested_origin| should only be a security origin (no path, etc.).
    169 // It is ok if |template_url| is NULL.
    170 static bool IsSameOrigin(const GURL& requested_origin,
    171                          TemplateURL* template_url,
    172                          const SearchTermsData& search_terms_data) {
    173   DCHECK(requested_origin == requested_origin.GetOrigin());
    174   DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
    175   return requested_origin ==
    176       template_url->GenerateSearchURL(search_terms_data).GetOrigin();
    177 }
    178 
    179 }  // namespace
    180 
    181 SearchProviderInstallData::SearchProviderInstallData(
    182     TemplateURLService* template_url_service,
    183     const std::string& google_base_url,
    184     GoogleURLTracker* google_url_tracker,
    185     content::RenderProcessHost* host)
    186     : template_url_service_(template_url_service),
    187       google_base_url_(google_base_url),
    188       weak_factory_(this) {
    189   // GoogleURLTracker is not created in tests.
    190   if (google_url_tracker) {
    191     // GoogleURLObserver is responsible for killing itself when
    192     // the given notification occurs.
    193     new GoogleURLObserver(
    194         google_url_tracker,
    195         new GoogleURLChangeNotifier(weak_factory_.GetWeakPtr()),
    196         host);
    197   }
    198 }
    199 
    200 SearchProviderInstallData::~SearchProviderInstallData() {
    201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    202 }
    203 
    204 void SearchProviderInstallData::CallWhenLoaded(const base::Closure& closure) {
    205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    206 
    207   if (provider_map_.get()) {
    208     closure.Run();
    209     return;
    210   }
    211 
    212   bool do_load = closure_queue_.empty();
    213   closure_queue_.push_back(closure);
    214 
    215   // If the queue wasn't empty, there was already a load in progress.
    216   if (!do_load)
    217     return;
    218 
    219   if (template_url_service_) {
    220     BrowserThread::PostTask(
    221         BrowserThread::UI,
    222         FROM_HERE,
    223         base::Bind(&LoadDataOnUIThread,
    224                    template_url_service_,
    225                    base::Bind(&SearchProviderInstallData::OnTemplateURLsLoaded,
    226                               weak_factory_.GetWeakPtr())));
    227   } else {
    228     OnLoadFailed();
    229   }
    230 }
    231 
    232 SearchProviderInstallData::State SearchProviderInstallData::GetInstallState(
    233     const GURL& requested_origin) {
    234   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    235   DCHECK(provider_map_.get());
    236 
    237   // First check to see if the origin is the default search provider.
    238   if (requested_origin.spec() == default_search_origin_)
    239     return INSTALLED_AS_DEFAULT;
    240 
    241   // Is the url any search provider?
    242   const TemplateURLSet* urls = provider_map_->GetURLsForHost(
    243       requested_origin.host());
    244   if (!urls)
    245     return NOT_INSTALLED;
    246 
    247   IOThreadSearchTermsData search_terms_data(google_base_url_);
    248   for (TemplateURLSet::const_iterator i = urls->begin();
    249        i != urls->end(); ++i) {
    250     if (IsSameOrigin(requested_origin, *i, search_terms_data))
    251       return INSTALLED_BUT_NOT_DEFAULT;
    252   }
    253   return NOT_INSTALLED;
    254 }
    255 
    256 void SearchProviderInstallData::OnGoogleURLChange(
    257     const std::string& google_base_url) {
    258   google_base_url_ = google_base_url;
    259 }
    260 
    261 void SearchProviderInstallData::OnTemplateURLsLoaded(
    262     ScopedVector<TemplateURL> template_urls,
    263     TemplateURL* default_provider) {
    264   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    265 
    266   template_urls_ = template_urls.Pass();
    267 
    268   IOThreadSearchTermsData search_terms_data(google_base_url_);
    269   provider_map_.reset(new SearchHostToURLsMap());
    270   provider_map_->Init(template_urls_.get(), search_terms_data);
    271   SetDefault(default_provider);
    272   NotifyLoaded();
    273 }
    274 
    275 void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) {
    276   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    277 
    278   if (!template_url) {
    279     default_search_origin_.clear();
    280     return;
    281   }
    282 
    283   DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
    284 
    285   IOThreadSearchTermsData search_terms_data(google_base_url_);
    286   const GURL url(template_url->GenerateSearchURL(search_terms_data));
    287   if (!url.is_valid() || !url.has_host()) {
    288     default_search_origin_.clear();
    289     return;
    290   }
    291   default_search_origin_ = url.GetOrigin().spec();
    292 }
    293 
    294 void SearchProviderInstallData::OnLoadFailed() {
    295   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    296 
    297   provider_map_.reset(new SearchHostToURLsMap());
    298   IOThreadSearchTermsData search_terms_data(google_base_url_);
    299   provider_map_->Init(template_urls_.get(), search_terms_data);
    300   SetDefault(NULL);
    301   NotifyLoaded();
    302 }
    303 
    304 void SearchProviderInstallData::NotifyLoaded() {
    305   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    306 
    307   std::vector<base::Closure> closure_queue;
    308   closure_queue.swap(closure_queue_);
    309 
    310   std::for_each(closure_queue.begin(),
    311                 closure_queue.end(),
    312                 std::mem_fun_ref(&base::Closure::Run));
    313 
    314   // Since we expect this request to be rare, clear out the information. This
    315   // also keeps the responses current as the search providers change.
    316   provider_map_.reset();
    317   SetDefault(NULL);
    318 }
    319