search_provider_install_data.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/logging.h" 14#include "base/memory/ref_counted.h" 15#include "base/sequenced_task_runner_helpers.h" 16#include "chrome/browser/google/google_url_tracker.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/search_engines/search_host_to_urls_map.h" 19#include "chrome/browser/search_engines/search_terms_data.h" 20#include "chrome/browser/search_engines/template_url.h" 21#include "chrome/browser/search_engines/template_url_service.h" 22#include "chrome/browser/search_engines/util.h" 23#include "chrome/browser/webdata/web_data_service.h" 24#include "chrome/common/chrome_notification_types.h" 25#include "content/public/browser/browser_thread.h" 26#include "content/public/browser/notification_observer.h" 27#include "content/public/browser/notification_registrar.h" 28#include "content/public/browser/notification_service.h" 29#include "content/public/browser/notification_source.h" 30 31using content::BrowserThread; 32 33typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet; 34 35namespace { 36 37// Implementation of SearchTermsData that may be used on the I/O thread. 38class IOThreadSearchTermsData : public SearchTermsData { 39 public: 40 explicit IOThreadSearchTermsData(const std::string& google_base_url); 41 42 // Implementation of SearchTermsData. 43 virtual std::string GoogleBaseURLValue() const OVERRIDE; 44 45 private: 46 std::string google_base_url_; 47 48 DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData); 49}; 50 51IOThreadSearchTermsData::IOThreadSearchTermsData( 52 const std::string& google_base_url) : google_base_url_(google_base_url) { 53} 54 55std::string IOThreadSearchTermsData::GoogleBaseURLValue() const { 56 return google_base_url_; 57} 58 59// Handles telling SearchProviderInstallData about changes to the google base 60// url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is 61// deleted on the correct thread.) 62class GoogleURLChangeNotifier 63 : public base::RefCountedThreadSafe<GoogleURLChangeNotifier, 64 BrowserThread::DeleteOnIOThread> { 65 public: 66 explicit GoogleURLChangeNotifier( 67 const base::WeakPtr<SearchProviderInstallData>& install_data); 68 69 // Called on the I/O thread with the Google base URL whenever the value 70 // changes. 71 void OnChange(const std::string& google_base_url); 72 73 private: 74 friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>; 75 friend class base::DeleteHelper<GoogleURLChangeNotifier>; 76 77 ~GoogleURLChangeNotifier() {} 78 79 base::WeakPtr<SearchProviderInstallData> install_data_; 80 81 DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier); 82}; 83 84GoogleURLChangeNotifier::GoogleURLChangeNotifier( 85 const base::WeakPtr<SearchProviderInstallData>& install_data) 86 : install_data_(install_data) { 87} 88 89void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 91 if (install_data_) 92 install_data_->OnGoogleURLChange(google_base_url); 93} 94 95// Notices changes in the Google base URL and sends them along 96// to the SearchProviderInstallData on the I/O thread. 97class GoogleURLObserver : public content::NotificationObserver { 98 public: 99 GoogleURLObserver(Profile* profile, 100 GoogleURLChangeNotifier* change_notifier, 101 int ui_death_notification, 102 const content::NotificationSource& ui_death_source); 103 104 // Implementation of content::NotificationObserver. 105 virtual void Observe(int type, 106 const content::NotificationSource& source, 107 const content::NotificationDetails& details) OVERRIDE; 108 109 private: 110 virtual ~GoogleURLObserver() {} 111 112 scoped_refptr<GoogleURLChangeNotifier> change_notifier_; 113 content::NotificationRegistrar registrar_; 114 115 DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver); 116}; 117 118GoogleURLObserver::GoogleURLObserver( 119 Profile* profile, 120 GoogleURLChangeNotifier* change_notifier, 121 int ui_death_notification, 122 const content::NotificationSource& ui_death_source) 123 : change_notifier_(change_notifier) { 124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 125 registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED, 126 content::Source<Profile>(profile->GetOriginalProfile())); 127 registrar_.Add(this, ui_death_notification, ui_death_source); 128} 129 130void GoogleURLObserver::Observe(int type, 131 const content::NotificationSource& source, 132 const content::NotificationDetails& details) { 133 if (type == chrome::NOTIFICATION_GOOGLE_URL_UPDATED) { 134 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 135 base::Bind(&GoogleURLChangeNotifier::OnChange, change_notifier_.get(), 136 content::Details<GoogleURLTracker::UpdatedDetails>(details)->second. 137 spec())); 138 } else { 139 // This must be the death notification. 140 delete this; 141 } 142} 143 144// Indicates if the two inputs have the same security origin. 145// |requested_origin| should only be a security origin (no path, etc.). 146// It is ok if |template_url| is NULL. 147static bool IsSameOrigin(const GURL& requested_origin, 148 TemplateURL* template_url, 149 const SearchTermsData& search_terms_data) { 150 DCHECK(requested_origin == requested_origin.GetOrigin()); 151 DCHECK(!template_url->IsExtensionKeyword()); 152 return requested_origin == 153 TemplateURLService::GenerateSearchURLUsingTermsData(template_url, 154 search_terms_data).GetOrigin(); 155} 156 157} // namespace 158 159SearchProviderInstallData::SearchProviderInstallData( 160 Profile* profile, 161 int ui_death_notification, 162 const content::NotificationSource& ui_death_source) 163 : web_service_(WebDataService::FromBrowserContext(profile)), 164 load_handle_(0), 165 google_base_url_(UIThreadSearchTermsData(profile).GoogleBaseURLValue()) { 166 // GoogleURLObserver is responsible for killing itself when 167 // the given notification occurs. 168 new GoogleURLObserver(profile, new GoogleURLChangeNotifier(AsWeakPtr()), 169 ui_death_notification, ui_death_source); 170} 171 172SearchProviderInstallData::~SearchProviderInstallData() { 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 174 175 if (load_handle_) { 176 DCHECK(web_service_.get()); 177 web_service_->CancelRequest(load_handle_); 178 } 179} 180 181void SearchProviderInstallData::CallWhenLoaded(const base::Closure& closure) { 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 183 184 if (provider_map_.get()) { 185 closure.Run(); 186 return; 187 } 188 189 closure_queue_.push_back(closure); 190 if (load_handle_) 191 return; 192 193 if (web_service_.get()) 194 load_handle_ = web_service_->GetKeywords(this); 195 else 196 OnLoadFailed(); 197} 198 199SearchProviderInstallData::State SearchProviderInstallData::GetInstallState( 200 const GURL& requested_origin) { 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 202 DCHECK(provider_map_.get()); 203 204 // First check to see if the origin is the default search provider. 205 if (requested_origin.spec() == default_search_origin_) 206 return INSTALLED_AS_DEFAULT; 207 208 // Is the url any search provider? 209 const TemplateURLSet* urls = provider_map_->GetURLsForHost( 210 requested_origin.host()); 211 if (!urls) 212 return NOT_INSTALLED; 213 214 IOThreadSearchTermsData search_terms_data(google_base_url_); 215 for (TemplateURLSet::const_iterator i = urls->begin(); 216 i != urls->end(); ++i) { 217 if (IsSameOrigin(requested_origin, *i, search_terms_data)) 218 return INSTALLED_BUT_NOT_DEFAULT; 219 } 220 return NOT_INSTALLED; 221} 222 223void SearchProviderInstallData::OnGoogleURLChange( 224 const std::string& google_base_url) { 225 google_base_url_ = google_base_url; 226} 227 228void SearchProviderInstallData::OnWebDataServiceRequestDone( 229 WebDataService::Handle h, 230 const WDTypedResult* result) { 231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 232 233 // Reset the load_handle so that we don't try and cancel the load in 234 // the destructor. 235 load_handle_ = 0; 236 237 if (!result) { 238 // Results are null if the database went away or (most likely) wasn't 239 // loaded. 240 OnLoadFailed(); 241 return; 242 } 243 244 TemplateURL* default_search_provider = NULL; 245 int new_resource_keyword_version = 0; 246 std::vector<TemplateURL*> extracted_template_urls; 247 GetSearchProvidersUsingKeywordResult(*result, NULL, NULL, 248 &extracted_template_urls, &default_search_provider, 249 &new_resource_keyword_version, NULL); 250 template_urls_.get().insert(template_urls_.get().begin(), 251 extracted_template_urls.begin(), 252 extracted_template_urls.end()); 253 IOThreadSearchTermsData search_terms_data(google_base_url_); 254 provider_map_.reset(new SearchHostToURLsMap()); 255 provider_map_->Init(template_urls_.get(), search_terms_data); 256 SetDefault(default_search_provider); 257 NotifyLoaded(); 258} 259 260void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) { 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 262 263 if (!template_url) { 264 default_search_origin_.clear(); 265 return; 266 } 267 268 DCHECK(!template_url->IsExtensionKeyword()); 269 270 IOThreadSearchTermsData search_terms_data(google_base_url_); 271 const GURL url(TemplateURLService::GenerateSearchURLUsingTermsData( 272 template_url, search_terms_data)); 273 if (!url.is_valid() || !url.has_host()) { 274 default_search_origin_.clear(); 275 return; 276 } 277 default_search_origin_ = url.GetOrigin().spec(); 278} 279 280void SearchProviderInstallData::OnLoadFailed() { 281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 282 283 provider_map_.reset(new SearchHostToURLsMap()); 284 IOThreadSearchTermsData search_terms_data(google_base_url_); 285 provider_map_->Init(template_urls_.get(), search_terms_data); 286 SetDefault(NULL); 287 NotifyLoaded(); 288} 289 290void SearchProviderInstallData::NotifyLoaded() { 291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 292 293 std::vector<base::Closure> closure_queue; 294 closure_queue.swap(closure_queue_); 295 296 std::for_each(closure_queue.begin(), 297 closure_queue.end(), 298 std::mem_fun_ref(&base::Closure::Run)); 299 300 // Since we expect this request to be rare, clear out the information. This 301 // also keeps the responses current as the search providers change. 302 provider_map_.reset(); 303 SetDefault(NULL); 304} 305