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.
5 #include "chrome/browser/search_engines/search_provider_install_data.h"
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/chrome_notification_types.h"
18 #include "chrome/browser/google/google_url_tracker.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
21 #include "chrome/browser/search_engines/search_terms_data.h"
22 #include "chrome/browser/search_engines/template_url.h"
23 #include "chrome/browser/search_engines/template_url_service.h"
24 #include "chrome/browser/search_engines/template_url_service_factory.h"
25 #include "chrome/browser/search_engines/util.h"
26 #include "chrome/browser/webdata/web_data_service.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_observer.h"
29 #include "content/public/browser/notification_registrar.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_source.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_process_host_observer.h"
35 using content::BrowserThread;
37 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
41 void LoadDataOnUIThread(TemplateURLService* template_url_service,
42 const base::Callback<void(ScopedVector<TemplateURL>,
43 TemplateURL*)>& callback) {
44 ScopedVector<TemplateURL> template_url_copies;
45 TemplateURL* default_provider_copy = NULL;
46 TemplateURLService::TemplateURLVector original_template_urls =
47 template_url_service->GetTemplateURLs();
48 TemplateURL* original_default_provider =
49 template_url_service->GetDefaultSearchProvider();
50 for (TemplateURLService::TemplateURLVector::const_iterator it =
51 original_template_urls.begin();
52 it != original_template_urls.end();
54 template_url_copies.push_back(new TemplateURL(NULL, (*it)->data()));
55 if (*it == original_default_provider)
56 default_provider_copy = template_url_copies.back();
58 BrowserThread::PostTask(BrowserThread::IO,
61 base::Passed(template_url_copies.Pass()),
62 base::Unretained(default_provider_copy)));
65 // Implementation of SearchTermsData that may be used on the I/O thread.
66 class IOThreadSearchTermsData : public SearchTermsData {
68 explicit IOThreadSearchTermsData(const std::string& google_base_url);
70 // Implementation of SearchTermsData.
71 virtual std::string GoogleBaseURLValue() const OVERRIDE;
74 std::string google_base_url_;
76 DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData);
79 IOThreadSearchTermsData::IOThreadSearchTermsData(
80 const std::string& google_base_url) : google_base_url_(google_base_url) {
83 std::string IOThreadSearchTermsData::GoogleBaseURLValue() const {
84 return google_base_url_;
87 // Handles telling SearchProviderInstallData about changes to the google base
88 // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is
89 // deleted on the correct thread.)
90 class GoogleURLChangeNotifier
91 : public base::RefCountedThreadSafe<GoogleURLChangeNotifier,
92 BrowserThread::DeleteOnIOThread> {
94 explicit GoogleURLChangeNotifier(
95 const base::WeakPtr<SearchProviderInstallData>& install_data);
97 // Called on the I/O thread with the Google base URL whenever the value
99 void OnChange(const std::string& google_base_url);
102 friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
103 friend class base::DeleteHelper<GoogleURLChangeNotifier>;
105 ~GoogleURLChangeNotifier() {}
107 base::WeakPtr<SearchProviderInstallData> install_data_;
109 DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier);
112 GoogleURLChangeNotifier::GoogleURLChangeNotifier(
113 const base::WeakPtr<SearchProviderInstallData>& install_data)
114 : install_data_(install_data) {
117 void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
119 if (install_data_.get())
120 install_data_->OnGoogleURLChange(google_base_url);
123 // Notices changes in the Google base URL and sends them along
124 // to the SearchProviderInstallData on the I/O thread.
125 class GoogleURLObserver : public content::NotificationObserver,
126 public content::RenderProcessHostObserver {
128 GoogleURLObserver(Profile* profile,
129 GoogleURLChangeNotifier* change_notifier,
130 content::RenderProcessHost* host);
132 // Implementation of content::NotificationObserver.
133 virtual void Observe(int type,
134 const content::NotificationSource& source,
135 const content::NotificationDetails& details) OVERRIDE;
137 // Implementation of content::RenderProcessHostObserver.
138 virtual void RenderProcessHostDestroyed(
139 content::RenderProcessHost* host) OVERRIDE;
142 virtual ~GoogleURLObserver() {}
144 scoped_refptr<GoogleURLChangeNotifier> change_notifier_;
145 content::NotificationRegistrar registrar_;
147 DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver);
150 GoogleURLObserver::GoogleURLObserver(
152 GoogleURLChangeNotifier* change_notifier,
153 content::RenderProcessHost* host)
154 : change_notifier_(change_notifier) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
157 content::Source<Profile>(profile->GetOriginalProfile()));
158 host->AddObserver(this);
161 void GoogleURLObserver::Observe(int type,
162 const content::NotificationSource& source,
163 const content::NotificationDetails& details) {
164 DCHECK_EQ(chrome::NOTIFICATION_GOOGLE_URL_UPDATED, type);
165 BrowserThread::PostTask(
166 BrowserThread::IO, FROM_HERE,
167 base::Bind(&GoogleURLChangeNotifier::OnChange,
168 change_notifier_.get(),
169 content::Details<GoogleURLTracker::UpdatedDetails>(details)->
173 void GoogleURLObserver::RenderProcessHostDestroyed(
174 content::RenderProcessHost* host) {
178 // Indicates if the two inputs have the same security origin.
179 // |requested_origin| should only be a security origin (no path, etc.).
180 // It is ok if |template_url| is NULL.
181 static bool IsSameOrigin(const GURL& requested_origin,
182 TemplateURL* template_url,
183 const SearchTermsData& search_terms_data) {
184 DCHECK(requested_origin == requested_origin.GetOrigin());
185 DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
186 return requested_origin ==
187 TemplateURLService::GenerateSearchURLUsingTermsData(template_url,
188 search_terms_data).GetOrigin();
193 SearchProviderInstallData::SearchProviderInstallData(
195 content::RenderProcessHost* host)
196 : template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)),
197 google_base_url_(UIThreadSearchTermsData(profile).GoogleBaseURLValue()),
198 weak_factory_(this) {
199 // GoogleURLObserver is responsible for killing itself when
200 // the given notification occurs.
201 new GoogleURLObserver(profile,
202 new GoogleURLChangeNotifier(weak_factory_.GetWeakPtr()),
206 SearchProviderInstallData::~SearchProviderInstallData() {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
210 void SearchProviderInstallData::CallWhenLoaded(const base::Closure& closure) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
213 if (provider_map_.get()) {
218 bool do_load = closure_queue_.empty();
219 closure_queue_.push_back(closure);
221 // If the queue wasn't empty, there was already a load in progress.
225 if (template_url_service_) {
226 BrowserThread::PostTask(
229 base::Bind(&LoadDataOnUIThread,
230 template_url_service_,
231 base::Bind(&SearchProviderInstallData::OnTemplateURLsLoaded,
232 weak_factory_.GetWeakPtr())));
238 SearchProviderInstallData::State SearchProviderInstallData::GetInstallState(
239 const GURL& requested_origin) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241 DCHECK(provider_map_.get());
243 // First check to see if the origin is the default search provider.
244 if (requested_origin.spec() == default_search_origin_)
245 return INSTALLED_AS_DEFAULT;
247 // Is the url any search provider?
248 const TemplateURLSet* urls = provider_map_->GetURLsForHost(
249 requested_origin.host());
251 return NOT_INSTALLED;
253 IOThreadSearchTermsData search_terms_data(google_base_url_);
254 for (TemplateURLSet::const_iterator i = urls->begin();
255 i != urls->end(); ++i) {
256 if (IsSameOrigin(requested_origin, *i, search_terms_data))
257 return INSTALLED_BUT_NOT_DEFAULT;
259 return NOT_INSTALLED;
262 void SearchProviderInstallData::OnGoogleURLChange(
263 const std::string& google_base_url) {
264 google_base_url_ = google_base_url;
267 void SearchProviderInstallData::OnTemplateURLsLoaded(
268 ScopedVector<TemplateURL> template_urls,
269 TemplateURL* default_provider) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
272 template_urls_ = template_urls.Pass();
274 IOThreadSearchTermsData search_terms_data(google_base_url_);
275 provider_map_.reset(new SearchHostToURLsMap());
276 provider_map_->Init(template_urls_.get(), search_terms_data);
277 SetDefault(default_provider);
281 void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
285 default_search_origin_.clear();
289 DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
291 IOThreadSearchTermsData search_terms_data(google_base_url_);
292 const GURL url(TemplateURLService::GenerateSearchURLUsingTermsData(
293 template_url, search_terms_data));
294 if (!url.is_valid() || !url.has_host()) {
295 default_search_origin_.clear();
298 default_search_origin_ = url.GetOrigin().spec();
301 void SearchProviderInstallData::OnLoadFailed() {
302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
304 provider_map_.reset(new SearchHostToURLsMap());
305 IOThreadSearchTermsData search_terms_data(google_base_url_);
306 provider_map_->Init(template_urls_.get(), search_terms_data);
311 void SearchProviderInstallData::NotifyLoaded() {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
314 std::vector<base::Closure> closure_queue;
315 closure_queue.swap(closure_queue_);
317 std::for_each(closure_queue.begin(),
319 std::mem_fun_ref(&base::Closure::Run));
321 // Since we expect this request to be rare, clear out the information. This
322 // also keeps the responses current as the search providers change.
323 provider_map_.reset();