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