| // 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 <algorithm> |
| #include <functional> |
| #include <vector> |
| |
| #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<void(ScopedVector<TemplateURL>, |
| TemplateURL*)>& callback) { |
| ScopedVector<TemplateURL> 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<GoogleURLChangeNotifier, |
| BrowserThread::DeleteOnIOThread> { |
| public: |
| explicit GoogleURLChangeNotifier( |
| const base::WeakPtr<SearchProviderInstallData>& 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<BrowserThread::IO>; |
| friend class base::DeleteHelper<GoogleURLChangeNotifier>; |
| |
| ~GoogleURLChangeNotifier() {} |
| |
| base::WeakPtr<SearchProviderInstallData> install_data_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier); |
| }; |
| |
| GoogleURLChangeNotifier::GoogleURLChangeNotifier( |
| const base::WeakPtr<SearchProviderInstallData>& 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<GoogleURLChangeNotifier> change_notifier_; |
| |
| scoped_ptr<GoogleURLTracker::Subscription> 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<TemplateURL> 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<base::Closure> 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); |
| } |