// Copyright (c) 2011 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 <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/task.h"
#include "chrome/browser/search_engines/search_host_to_urls_map.h"
#include "chrome/browser/search_engines/search_terms_data.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/search_engines/util.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "content/common/notification_service.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
namespace {
// 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.
virtual std::string GoogleBaseURLValue() const;
virtual std::string GetApplicationLocale() const;
#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
virtual string16 GetRlzParameterValue() const {
// This value doesn't matter for our purposes.
return string16();
}
#endif
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_;
}
std::string IOThreadSearchTermsData::GetApplicationLocale() const {
// This value doesn't matter for our purposes.
return "yy";
}
// 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 DeleteTask<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_)
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 NotificationObserver {
public:
GoogleURLObserver(
GoogleURLChangeNotifier* change_notifier,
NotificationType ui_death_notification,
const NotificationSource& ui_death_source);
// Implementation of NotificationObserver.
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
private:
virtual ~GoogleURLObserver() {}
scoped_refptr<GoogleURLChangeNotifier> change_notifier_;
NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver);
};
GoogleURLObserver::GoogleURLObserver(
GoogleURLChangeNotifier* change_notifier,
NotificationType ui_death_notification,
const NotificationSource& ui_death_source)
: change_notifier_(change_notifier) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
registrar_.Add(this, NotificationType::GOOGLE_URL_UPDATED,
NotificationService::AllSources());
registrar_.Add(this, ui_death_notification, ui_death_source);
}
void GoogleURLObserver::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::GOOGLE_URL_UPDATED) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
NewRunnableMethod(change_notifier_.get(),
&GoogleURLChangeNotifier::OnChange,
UIThreadSearchTermsData().GoogleBaseURLValue()));
} else {
// This must be the death notification.
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,
const TemplateURL* template_url,
const SearchTermsData& search_terms_data) {
DCHECK(requested_origin == requested_origin.GetOrigin());
return template_url && requested_origin ==
TemplateURLModel::GenerateSearchURLUsingTermsData(
template_url,
search_terms_data).GetOrigin();
}
} // namespace
SearchProviderInstallData::SearchProviderInstallData(
WebDataService* web_service,
NotificationType ui_death_notification,
const NotificationSource& ui_death_source)
: web_service_(web_service),
load_handle_(0),
google_base_url_(UIThreadSearchTermsData().GoogleBaseURLValue()) {
// GoogleURLObserver is responsible for killing itself when
// the given notification occurs.
new GoogleURLObserver(new GoogleURLChangeNotifier(AsWeakPtr()),
ui_death_notification, ui_death_source);
DetachFromThread();
}
SearchProviderInstallData::~SearchProviderInstallData() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (load_handle_) {
DCHECK(web_service_.get());
web_service_->CancelRequest(load_handle_);
}
}
void SearchProviderInstallData::CallWhenLoaded(Task* task) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (provider_map_.get()) {
task->Run();
delete task;
return;
}
task_queue_.Push(task);
if (load_handle_)
return;
if (web_service_.get())
load_handle_ = web_service_->GetKeywords(this);
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) {
const TemplateURL* template_url = *i;
if (IsSameOrigin(requested_origin, template_url, 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::OnWebDataServiceRequestDone(
WebDataService::Handle h,
const WDTypedResult* result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Reset the load_handle so that we don't try and cancel the load in
// the destructor.
load_handle_ = 0;
if (!result) {
// Results are null if the database went away or (most likely) wasn't
// loaded.
OnLoadFailed();
return;
}
const TemplateURL* default_search_provider = NULL;
int new_resource_keyword_version = 0;
std::vector<TemplateURL*> extracted_template_urls;
GetSearchProvidersUsingKeywordResult(*result,
NULL,
NULL,
&extracted_template_urls,
&default_search_provider,
&new_resource_keyword_version);
template_urls_.get().insert(template_urls_.get().begin(),
extracted_template_urls.begin(),
extracted_template_urls.end());
IOThreadSearchTermsData search_terms_data(google_base_url_);
provider_map_.reset(new SearchHostToURLsMap());
provider_map_->Init(template_urls_.get(), search_terms_data);
SetDefault(default_search_provider);
NotifyLoaded();
}
void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!template_url) {
default_search_origin_.clear();
return;
}
IOThreadSearchTermsData search_terms_data(google_base_url_);
const GURL url(TemplateURLModel::GenerateSearchURLUsingTermsData(
template_url, 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));
task_queue_.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);
}