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