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 "chrome/browser/google/google_url_tracker_factory.h"
     18 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
     19 #include "chrome/browser/search_engines/template_url.h"
     20 #include "chrome/browser/search_engines/template_url_service.h"
     21 #include "chrome/browser/search_engines/template_url_service_factory.h"
     22 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
     23 #include "chrome/browser/search_engines/util.h"
     24 #include "components/google/core/browser/google_url_tracker.h"
     25 #include "content/public/browser/browser_thread.h"
     26 #include "content/public/browser/render_process_host.h"
     27 #include "content/public/browser/render_process_host_observer.h"
     28 
     29 using content::BrowserThread;
     30 
     31 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
     32 
     33 namespace {
     34 
     35 void LoadDataOnUIThread(TemplateURLService* template_url_service,
     36                         const base::Callback<void(ScopedVector<TemplateURL>,
     37                                                   TemplateURL*)>& callback) {
     38   ScopedVector<TemplateURL> template_url_copies;
     39   TemplateURL* default_provider_copy = NULL;
     40   TemplateURLService::TemplateURLVector original_template_urls =
     41       template_url_service->GetTemplateURLs();
     42   TemplateURL* original_default_provider =
     43       template_url_service->GetDefaultSearchProvider();
     44   for (TemplateURLService::TemplateURLVector::const_iterator it =
     45            original_template_urls.begin();
     46        it != original_template_urls.end();
     47        ++it) {
     48     template_url_copies.push_back(new TemplateURL((*it)->data()));
     49     if (*it == original_default_provider)
     50       default_provider_copy = template_url_copies.back();
     51   }
     52   BrowserThread::PostTask(BrowserThread::IO,
     53                           FROM_HERE,
     54                           base::Bind(callback,
     55                                      base::Passed(template_url_copies.Pass()),
     56                                      base::Unretained(default_provider_copy)));
     57 }
     58 
     59 // Implementation of SearchTermsData that may be used on the I/O thread.
     60 class IOThreadSearchTermsData : public SearchTermsData {
     61  public:
     62   explicit IOThreadSearchTermsData(const std::string& google_base_url);
     63 
     64   // Implementation of SearchTermsData.
     65   virtual std::string GoogleBaseURLValue() const OVERRIDE;
     66 
     67  private:
     68   std::string google_base_url_;
     69 
     70   DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData);
     71 };
     72 
     73 IOThreadSearchTermsData::IOThreadSearchTermsData(
     74     const std::string& google_base_url) : google_base_url_(google_base_url) {
     75 }
     76 
     77 std::string IOThreadSearchTermsData::GoogleBaseURLValue() const {
     78   return google_base_url_;
     79 }
     80 
     81 // Handles telling SearchProviderInstallData about changes to the google base
     82 // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is
     83 // deleted on the correct thread.)
     84 class GoogleURLChangeNotifier
     85     : public base::RefCountedThreadSafe<GoogleURLChangeNotifier,
     86                                         BrowserThread::DeleteOnIOThread> {
     87  public:
     88   explicit GoogleURLChangeNotifier(
     89       const base::WeakPtr<SearchProviderInstallData>& install_data);
     90 
     91   // Called on the I/O thread with the Google base URL whenever the value
     92   // changes.
     93   void OnChange(const std::string& google_base_url);
     94 
     95  private:
     96   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
     97   friend class base::DeleteHelper<GoogleURLChangeNotifier>;
     98 
     99   ~GoogleURLChangeNotifier() {}
    100 
    101   base::WeakPtr<SearchProviderInstallData> install_data_;
    102 
    103   DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier);
    104 };
    105 
    106 GoogleURLChangeNotifier::GoogleURLChangeNotifier(
    107     const base::WeakPtr<SearchProviderInstallData>& install_data)
    108     : install_data_(install_data) {
    109 }
    110 
    111 void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) {
    112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    113   if (install_data_.get())
    114     install_data_->OnGoogleURLChange(google_base_url);
    115 }
    116 
    117 // Notices changes in the Google base URL and sends them along
    118 // to the SearchProviderInstallData on the I/O thread.
    119 class GoogleURLObserver : public content::RenderProcessHostObserver {
    120  public:
    121   GoogleURLObserver(Profile* profile,
    122                     GoogleURLChangeNotifier* change_notifier,
    123                     content::RenderProcessHost* host);
    124 
    125   // Implementation of content::RenderProcessHostObserver.
    126   virtual void RenderProcessHostDestroyed(
    127         content::RenderProcessHost* host) OVERRIDE;
    128 
    129  private:
    130   virtual ~GoogleURLObserver() {}
    131 
    132   // Callback that is called when the Google URL is updated.
    133   void OnGoogleURLUpdated(GURL old_url, GURL new_url);
    134 
    135   scoped_refptr<GoogleURLChangeNotifier> change_notifier_;
    136 
    137   scoped_ptr<GoogleURLTracker::Subscription> google_url_updated_subscription_;
    138 
    139   DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver);
    140 };
    141 
    142 GoogleURLObserver::GoogleURLObserver(
    143     Profile* profile,
    144     GoogleURLChangeNotifier* change_notifier,
    145     content::RenderProcessHost* host)
    146     : change_notifier_(change_notifier) {
    147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    148   GoogleURLTracker* google_url_tracker =
    149       GoogleURLTrackerFactory::GetForProfile(profile);
    150 
    151   // GoogleURLTracker is not created in tests.
    152   if (google_url_tracker) {
    153     google_url_updated_subscription_ =
    154         google_url_tracker->RegisterCallback(base::Bind(
    155             &GoogleURLObserver::OnGoogleURLUpdated, base::Unretained(this)));
    156   }
    157   host->AddObserver(this);
    158 }
    159 
    160 void GoogleURLObserver::OnGoogleURLUpdated(GURL old_url, GURL new_url) {
    161   BrowserThread::PostTask(BrowserThread::IO,
    162                           FROM_HERE,
    163                           base::Bind(&GoogleURLChangeNotifier::OnChange,
    164                                      change_notifier_.get(),
    165                                      new_url.spec()));
    166 }
    167 
    168 void GoogleURLObserver::RenderProcessHostDestroyed(
    169     content::RenderProcessHost* host) {
    170   delete this;
    171 }
    172 
    173 // Indicates if the two inputs have the same security origin.
    174 // |requested_origin| should only be a security origin (no path, etc.).
    175 // It is ok if |template_url| is NULL.
    176 static bool IsSameOrigin(const GURL& requested_origin,
    177                          TemplateURL* template_url,
    178                          const SearchTermsData& search_terms_data) {
    179   DCHECK(requested_origin == requested_origin.GetOrigin());
    180   DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
    181   return requested_origin ==
    182       template_url->GenerateSearchURL(search_terms_data).GetOrigin();
    183 }
    184 
    185 }  // namespace
    186 
    187 SearchProviderInstallData::SearchProviderInstallData(
    188     Profile* profile,
    189     content::RenderProcessHost* host)
    190     : template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)),
    191       google_base_url_(UIThreadSearchTermsData(profile).GoogleBaseURLValue()),
    192       weak_factory_(this) {
    193   // GoogleURLObserver is responsible for killing itself when
    194   // the given notification occurs.
    195   new GoogleURLObserver(profile,
    196                         new GoogleURLChangeNotifier(weak_factory_.GetWeakPtr()),
    197                         host);
    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