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 "components/google/core/browser/google_url_tracker.h"
18 #include "components/search_engines/search_host_to_urls_map.h"
19 #include "components/search_engines/search_terms_data.h"
20 #include "components/search_engines/template_url.h"
21 #include "components/search_engines/template_url_service.h"
22 #include "components/search_engines/util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_process_host_observer.h"
27 using content::BrowserThread
;
29 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet
;
33 void LoadDataOnUIThread(TemplateURLService
* template_url_service
,
34 const base::Callback
<void(ScopedVector
<TemplateURL
>,
35 TemplateURL
*)>& callback
) {
36 ScopedVector
<TemplateURL
> template_url_copies
;
37 TemplateURL
* default_provider_copy
= NULL
;
38 TemplateURLService::TemplateURLVector original_template_urls
=
39 template_url_service
->GetTemplateURLs();
40 TemplateURL
* original_default_provider
=
41 template_url_service
->GetDefaultSearchProvider();
42 for (TemplateURLService::TemplateURLVector::const_iterator it
=
43 original_template_urls
.begin();
44 it
!= original_template_urls
.end();
46 template_url_copies
.push_back(new TemplateURL((*it
)->data()));
47 if (*it
== original_default_provider
)
48 default_provider_copy
= template_url_copies
.back();
50 BrowserThread::PostTask(BrowserThread::IO
,
53 base::Passed(template_url_copies
.Pass()),
54 base::Unretained(default_provider_copy
)));
57 // Implementation of SearchTermsData that may be used on the I/O thread.
58 class IOThreadSearchTermsData
: public SearchTermsData
{
60 explicit IOThreadSearchTermsData(const std::string
& google_base_url
);
62 // Implementation of SearchTermsData.
63 virtual std::string
GoogleBaseURLValue() const override
;
66 std::string google_base_url_
;
68 DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData
);
71 IOThreadSearchTermsData::IOThreadSearchTermsData(
72 const std::string
& google_base_url
) : google_base_url_(google_base_url
) {
75 std::string
IOThreadSearchTermsData::GoogleBaseURLValue() const {
76 return google_base_url_
;
79 // Handles telling SearchProviderInstallData about changes to the google base
80 // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is
81 // deleted on the correct thread.)
82 class GoogleURLChangeNotifier
83 : public base::RefCountedThreadSafe
<GoogleURLChangeNotifier
,
84 BrowserThread::DeleteOnIOThread
> {
86 explicit GoogleURLChangeNotifier(
87 const base::WeakPtr
<SearchProviderInstallData
>& install_data
);
89 // Called on the I/O thread with the Google base URL whenever the value
91 void OnChange(const std::string
& google_base_url
);
94 friend struct BrowserThread::DeleteOnThread
<BrowserThread::IO
>;
95 friend class base::DeleteHelper
<GoogleURLChangeNotifier
>;
97 ~GoogleURLChangeNotifier() {}
99 base::WeakPtr
<SearchProviderInstallData
> install_data_
;
101 DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier
);
104 GoogleURLChangeNotifier::GoogleURLChangeNotifier(
105 const base::WeakPtr
<SearchProviderInstallData
>& install_data
)
106 : install_data_(install_data
) {
109 void GoogleURLChangeNotifier::OnChange(const std::string
& google_base_url
) {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
111 if (install_data_
.get())
112 install_data_
->OnGoogleURLChange(google_base_url
);
115 // Notices changes in the Google base URL and sends them along
116 // to the SearchProviderInstallData on the I/O thread.
117 class GoogleURLObserver
: public content::RenderProcessHostObserver
{
119 GoogleURLObserver(GoogleURLTracker
* google_url_tracker
,
120 GoogleURLChangeNotifier
* change_notifier
,
121 content::RenderProcessHost
* host
);
123 // Implementation of content::RenderProcessHostObserver.
124 virtual void RenderProcessHostDestroyed(
125 content::RenderProcessHost
* host
) override
;
128 virtual ~GoogleURLObserver() {}
130 // Callback that is called when the Google URL is updated.
131 void OnGoogleURLUpdated();
133 GoogleURLTracker
* google_url_tracker_
;
134 scoped_refptr
<GoogleURLChangeNotifier
> change_notifier_
;
136 scoped_ptr
<GoogleURLTracker::Subscription
> google_url_updated_subscription_
;
138 DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver
);
141 GoogleURLObserver::GoogleURLObserver(
142 GoogleURLTracker
* google_url_tracker
,
143 GoogleURLChangeNotifier
* change_notifier
,
144 content::RenderProcessHost
* host
)
145 : google_url_tracker_(google_url_tracker
),
146 change_notifier_(change_notifier
) {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
148 google_url_updated_subscription_
=
149 google_url_tracker_
->RegisterCallback(base::Bind(
150 &GoogleURLObserver::OnGoogleURLUpdated
, base::Unretained(this)));
151 host
->AddObserver(this);
154 void GoogleURLObserver::OnGoogleURLUpdated() {
155 BrowserThread::PostTask(BrowserThread::IO
,
157 base::Bind(&GoogleURLChangeNotifier::OnChange
,
158 change_notifier_
.get(),
159 google_url_tracker_
->google_url().spec()));
162 void GoogleURLObserver::RenderProcessHostDestroyed(
163 content::RenderProcessHost
* host
) {
167 // Indicates if the two inputs have the same security origin.
168 // |requested_origin| should only be a security origin (no path, etc.).
169 // It is ok if |template_url| is NULL.
170 static bool IsSameOrigin(const GURL
& requested_origin
,
171 TemplateURL
* template_url
,
172 const SearchTermsData
& search_terms_data
) {
173 DCHECK(requested_origin
== requested_origin
.GetOrigin());
174 DCHECK(template_url
->GetType() != TemplateURL::OMNIBOX_API_EXTENSION
);
175 return requested_origin
==
176 template_url
->GenerateSearchURL(search_terms_data
).GetOrigin();
181 SearchProviderInstallData::SearchProviderInstallData(
182 TemplateURLService
* template_url_service
,
183 const std::string
& google_base_url
,
184 GoogleURLTracker
* google_url_tracker
,
185 content::RenderProcessHost
* host
)
186 : template_url_service_(template_url_service
),
187 google_base_url_(google_base_url
),
188 weak_factory_(this) {
189 // GoogleURLTracker is not created in tests.
190 if (google_url_tracker
) {
191 // GoogleURLObserver is responsible for killing itself when
192 // the given notification occurs.
193 new GoogleURLObserver(
195 new GoogleURLChangeNotifier(weak_factory_
.GetWeakPtr()),
200 SearchProviderInstallData::~SearchProviderInstallData() {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
204 void SearchProviderInstallData::CallWhenLoaded(const base::Closure
& closure
) {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
207 if (provider_map_
.get()) {
212 bool do_load
= closure_queue_
.empty();
213 closure_queue_
.push_back(closure
);
215 // If the queue wasn't empty, there was already a load in progress.
219 if (template_url_service_
) {
220 BrowserThread::PostTask(
223 base::Bind(&LoadDataOnUIThread
,
224 template_url_service_
,
225 base::Bind(&SearchProviderInstallData::OnTemplateURLsLoaded
,
226 weak_factory_
.GetWeakPtr())));
232 SearchProviderInstallData::State
SearchProviderInstallData::GetInstallState(
233 const GURL
& requested_origin
) {
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
235 DCHECK(provider_map_
.get());
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
;
241 // Is the url any search provider?
242 const TemplateURLSet
* urls
= provider_map_
->GetURLsForHost(
243 requested_origin
.host());
245 return NOT_INSTALLED
;
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
;
253 return NOT_INSTALLED
;
256 void SearchProviderInstallData::OnGoogleURLChange(
257 const std::string
& google_base_url
) {
258 google_base_url_
= google_base_url
;
261 void SearchProviderInstallData::OnTemplateURLsLoaded(
262 ScopedVector
<TemplateURL
> template_urls
,
263 TemplateURL
* default_provider
) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
266 template_urls_
= template_urls
.Pass();
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
);
275 void SearchProviderInstallData::SetDefault(const TemplateURL
* template_url
) {
276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
279 default_search_origin_
.clear();
283 DCHECK(template_url
->GetType() != TemplateURL::OMNIBOX_API_EXTENSION
);
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();
291 default_search_origin_
= url
.GetOrigin().spec();
294 void SearchProviderInstallData::OnLoadFailed() {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
297 provider_map_
.reset(new SearchHostToURLsMap());
298 IOThreadSearchTermsData
search_terms_data(google_base_url_
);
299 provider_map_
->Init(template_urls_
.get(), search_terms_data
);
304 void SearchProviderInstallData::NotifyLoaded() {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
307 std::vector
<base::Closure
> closure_queue
;
308 closure_queue
.swap(closure_queue_
);
310 std::for_each(closure_queue
.begin(),
312 std::mem_fun_ref(&base::Closure::Run
));
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();