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