// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/search_engines/search_provider_install_data.h" #include #include #include #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner_helpers.h" #include "components/google/core/browser/google_url_tracker.h" #include "components/search_engines/search_host_to_urls_map.h" #include "components/search_engines/search_terms_data.h" #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_service.h" #include "components/search_engines/util.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host_observer.h" using content::BrowserThread; typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet; namespace { void LoadDataOnUIThread(TemplateURLService* template_url_service, const base::Callback, TemplateURL*)>& callback) { ScopedVector template_url_copies; TemplateURL* default_provider_copy = NULL; TemplateURLService::TemplateURLVector original_template_urls = template_url_service->GetTemplateURLs(); TemplateURL* original_default_provider = template_url_service->GetDefaultSearchProvider(); for (TemplateURLService::TemplateURLVector::const_iterator it = original_template_urls.begin(); it != original_template_urls.end(); ++it) { template_url_copies.push_back(new TemplateURL((*it)->data())); if (*it == original_default_provider) default_provider_copy = template_url_copies.back(); } BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(callback, base::Passed(template_url_copies.Pass()), base::Unretained(default_provider_copy))); } // Implementation of SearchTermsData that may be used on the I/O thread. class IOThreadSearchTermsData : public SearchTermsData { public: explicit IOThreadSearchTermsData(const std::string& google_base_url); // Implementation of SearchTermsData. std::string GoogleBaseURLValue() const override; private: std::string google_base_url_; DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData); }; IOThreadSearchTermsData::IOThreadSearchTermsData( const std::string& google_base_url) : google_base_url_(google_base_url) { } std::string IOThreadSearchTermsData::GoogleBaseURLValue() const { return google_base_url_; } // Handles telling SearchProviderInstallData about changes to the google base // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is // deleted on the correct thread.) class GoogleURLChangeNotifier : public base::RefCountedThreadSafe { public: explicit GoogleURLChangeNotifier( const base::WeakPtr& install_data); // Called on the I/O thread with the Google base URL whenever the value // changes. void OnChange(const std::string& google_base_url); private: friend struct BrowserThread::DeleteOnThread; friend class base::DeleteHelper; ~GoogleURLChangeNotifier() {} base::WeakPtr install_data_; DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier); }; GoogleURLChangeNotifier::GoogleURLChangeNotifier( const base::WeakPtr& install_data) : install_data_(install_data) { } void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (install_data_.get()) install_data_->OnGoogleURLChange(google_base_url); } // Notices changes in the Google base URL and sends them along // to the SearchProviderInstallData on the I/O thread. class GoogleURLObserver : public content::RenderProcessHostObserver { public: GoogleURLObserver(GoogleURLTracker* google_url_tracker, GoogleURLChangeNotifier* change_notifier, content::RenderProcessHost* host); // Implementation of content::RenderProcessHostObserver. void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; private: ~GoogleURLObserver() override {} // Callback that is called when the Google URL is updated. void OnGoogleURLUpdated(); GoogleURLTracker* google_url_tracker_; scoped_refptr change_notifier_; scoped_ptr google_url_updated_subscription_; DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver); }; GoogleURLObserver::GoogleURLObserver( GoogleURLTracker* google_url_tracker, GoogleURLChangeNotifier* change_notifier, content::RenderProcessHost* host) : google_url_tracker_(google_url_tracker), change_notifier_(change_notifier) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); google_url_updated_subscription_ = google_url_tracker_->RegisterCallback(base::Bind( &GoogleURLObserver::OnGoogleURLUpdated, base::Unretained(this))); host->AddObserver(this); } void GoogleURLObserver::OnGoogleURLUpdated() { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&GoogleURLChangeNotifier::OnChange, change_notifier_.get(), google_url_tracker_->google_url().spec())); } void GoogleURLObserver::RenderProcessHostDestroyed( content::RenderProcessHost* host) { delete this; } // Indicates if the two inputs have the same security origin. // |requested_origin| should only be a security origin (no path, etc.). // It is ok if |template_url| is NULL. static bool IsSameOrigin(const GURL& requested_origin, TemplateURL* template_url, const SearchTermsData& search_terms_data) { DCHECK(requested_origin == requested_origin.GetOrigin()); DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION); return requested_origin == template_url->GenerateSearchURL(search_terms_data).GetOrigin(); } } // namespace SearchProviderInstallData::SearchProviderInstallData( TemplateURLService* template_url_service, const std::string& google_base_url, GoogleURLTracker* google_url_tracker, content::RenderProcessHost* host) : template_url_service_(template_url_service), google_base_url_(google_base_url), weak_factory_(this) { // GoogleURLTracker is not created in tests. if (google_url_tracker) { // GoogleURLObserver is responsible for killing itself when // the given notification occurs. new GoogleURLObserver( google_url_tracker, new GoogleURLChangeNotifier(weak_factory_.GetWeakPtr()), host); } } SearchProviderInstallData::~SearchProviderInstallData() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); } void SearchProviderInstallData::CallWhenLoaded(const base::Closure& closure) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (provider_map_.get()) { closure.Run(); return; } bool do_load = closure_queue_.empty(); closure_queue_.push_back(closure); // If the queue wasn't empty, there was already a load in progress. if (!do_load) return; if (template_url_service_) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&LoadDataOnUIThread, template_url_service_, base::Bind(&SearchProviderInstallData::OnTemplateURLsLoaded, weak_factory_.GetWeakPtr()))); } else { OnLoadFailed(); } } SearchProviderInstallData::State SearchProviderInstallData::GetInstallState( const GURL& requested_origin) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(provider_map_.get()); // First check to see if the origin is the default search provider. if (requested_origin.spec() == default_search_origin_) return INSTALLED_AS_DEFAULT; // Is the url any search provider? const TemplateURLSet* urls = provider_map_->GetURLsForHost( requested_origin.host()); if (!urls) return NOT_INSTALLED; IOThreadSearchTermsData search_terms_data(google_base_url_); for (TemplateURLSet::const_iterator i = urls->begin(); i != urls->end(); ++i) { if (IsSameOrigin(requested_origin, *i, search_terms_data)) return INSTALLED_BUT_NOT_DEFAULT; } return NOT_INSTALLED; } void SearchProviderInstallData::OnGoogleURLChange( const std::string& google_base_url) { google_base_url_ = google_base_url; } void SearchProviderInstallData::OnTemplateURLsLoaded( ScopedVector template_urls, TemplateURL* default_provider) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); template_urls_ = template_urls.Pass(); IOThreadSearchTermsData search_terms_data(google_base_url_); provider_map_.reset(new SearchHostToURLsMap()); provider_map_->Init(template_urls_.get(), search_terms_data); SetDefault(default_provider); NotifyLoaded(); } void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!template_url) { default_search_origin_.clear(); return; } DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION); IOThreadSearchTermsData search_terms_data(google_base_url_); const GURL url(template_url->GenerateSearchURL(search_terms_data)); if (!url.is_valid() || !url.has_host()) { default_search_origin_.clear(); return; } default_search_origin_ = url.GetOrigin().spec(); } void SearchProviderInstallData::OnLoadFailed() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); provider_map_.reset(new SearchHostToURLsMap()); IOThreadSearchTermsData search_terms_data(google_base_url_); provider_map_->Init(template_urls_.get(), search_terms_data); SetDefault(NULL); NotifyLoaded(); } void SearchProviderInstallData::NotifyLoaded() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); std::vector closure_queue; closure_queue.swap(closure_queue_); std::for_each(closure_queue.begin(), closure_queue.end(), std::mem_fun_ref(&base::Closure::Run)); // Since we expect this request to be rare, clear out the information. This // also keeps the responses current as the search providers change. provider_map_.reset(); SetDefault(NULL); }