- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / search_engines / search_provider_install_data.cc
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.
4
5 #include "chrome/browser/search_engines/search_provider_install_data.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <vector>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/sequenced_task_runner_helpers.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/google/google_url_tracker.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
20 #include "chrome/browser/search_engines/search_terms_data.h"
21 #include "chrome/browser/search_engines/template_url.h"
22 #include "chrome/browser/search_engines/template_url_service.h"
23 #include "chrome/browser/search_engines/util.h"
24 #include "chrome/browser/webdata/web_data_service.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_observer.h"
27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/notification_source.h"
30
31 using content::BrowserThread;
32
33 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
34
35 namespace {
36
37 // Implementation of SearchTermsData that may be used on the I/O thread.
38 class IOThreadSearchTermsData : public SearchTermsData {
39  public:
40   explicit IOThreadSearchTermsData(const std::string& google_base_url);
41
42   // Implementation of SearchTermsData.
43   virtual std::string GoogleBaseURLValue() const OVERRIDE;
44
45  private:
46   std::string google_base_url_;
47
48   DISALLOW_COPY_AND_ASSIGN(IOThreadSearchTermsData);
49 };
50
51 IOThreadSearchTermsData::IOThreadSearchTermsData(
52     const std::string& google_base_url) : google_base_url_(google_base_url) {
53 }
54
55 std::string IOThreadSearchTermsData::GoogleBaseURLValue() const {
56   return google_base_url_;
57 }
58
59 // Handles telling SearchProviderInstallData about changes to the google base
60 // url. (Ensure that this is deleted on the I/O thread so that the WeakPtr is
61 // deleted on the correct thread.)
62 class GoogleURLChangeNotifier
63     : public base::RefCountedThreadSafe<GoogleURLChangeNotifier,
64                                         BrowserThread::DeleteOnIOThread> {
65  public:
66   explicit GoogleURLChangeNotifier(
67       const base::WeakPtr<SearchProviderInstallData>& install_data);
68
69   // Called on the I/O thread with the Google base URL whenever the value
70   // changes.
71   void OnChange(const std::string& google_base_url);
72
73  private:
74   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
75   friend class base::DeleteHelper<GoogleURLChangeNotifier>;
76
77   ~GoogleURLChangeNotifier() {}
78
79   base::WeakPtr<SearchProviderInstallData> install_data_;
80
81   DISALLOW_COPY_AND_ASSIGN(GoogleURLChangeNotifier);
82 };
83
84 GoogleURLChangeNotifier::GoogleURLChangeNotifier(
85     const base::WeakPtr<SearchProviderInstallData>& install_data)
86     : install_data_(install_data) {
87 }
88
89 void GoogleURLChangeNotifier::OnChange(const std::string& google_base_url) {
90   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
91   if (install_data_.get())
92     install_data_->OnGoogleURLChange(google_base_url);
93 }
94
95 // Notices changes in the Google base URL and sends them along
96 // to the SearchProviderInstallData on the I/O thread.
97 class GoogleURLObserver : public content::NotificationObserver {
98  public:
99   GoogleURLObserver(Profile* profile,
100                     GoogleURLChangeNotifier* change_notifier,
101                     int ui_death_notification,
102                     const content::NotificationSource& ui_death_source);
103
104   // Implementation of content::NotificationObserver.
105   virtual void Observe(int type,
106                        const content::NotificationSource& source,
107                        const content::NotificationDetails& details) OVERRIDE;
108
109  private:
110   virtual ~GoogleURLObserver() {}
111
112   scoped_refptr<GoogleURLChangeNotifier> change_notifier_;
113   content::NotificationRegistrar registrar_;
114
115   DISALLOW_COPY_AND_ASSIGN(GoogleURLObserver);
116 };
117
118 GoogleURLObserver::GoogleURLObserver(
119     Profile* profile,
120     GoogleURLChangeNotifier* change_notifier,
121     int ui_death_notification,
122     const content::NotificationSource& ui_death_source)
123     : change_notifier_(change_notifier) {
124   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
125   registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
126                  content::Source<Profile>(profile->GetOriginalProfile()));
127   registrar_.Add(this, ui_death_notification, ui_death_source);
128 }
129
130 void GoogleURLObserver::Observe(int type,
131                                 const content::NotificationSource& source,
132                                 const content::NotificationDetails& details) {
133   if (type == chrome::NOTIFICATION_GOOGLE_URL_UPDATED) {
134     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
135         base::Bind(&GoogleURLChangeNotifier::OnChange, change_notifier_.get(),
136             content::Details<GoogleURLTracker::UpdatedDetails>(details)->second.
137                 spec()));
138   } else {
139     // This must be the death notification.
140     delete this;
141   }
142 }
143
144 // Indicates if the two inputs have the same security origin.
145 // |requested_origin| should only be a security origin (no path, etc.).
146 // It is ok if |template_url| is NULL.
147 static bool IsSameOrigin(const GURL& requested_origin,
148                          TemplateURL* template_url,
149                          const SearchTermsData& search_terms_data) {
150   DCHECK(requested_origin == requested_origin.GetOrigin());
151   DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
152   return requested_origin ==
153       TemplateURLService::GenerateSearchURLUsingTermsData(template_url,
154           search_terms_data).GetOrigin();
155 }
156
157 }  // namespace
158
159 SearchProviderInstallData::SearchProviderInstallData(
160     Profile* profile,
161     int ui_death_notification,
162     const content::NotificationSource& ui_death_source)
163     : web_service_(WebDataService::FromBrowserContext(profile)),
164       load_handle_(0),
165       google_base_url_(UIThreadSearchTermsData(profile).GoogleBaseURLValue()) {
166   // GoogleURLObserver is responsible for killing itself when
167   // the given notification occurs.
168   new GoogleURLObserver(profile, new GoogleURLChangeNotifier(AsWeakPtr()),
169                         ui_death_notification, ui_death_source);
170 }
171
172 SearchProviderInstallData::~SearchProviderInstallData() {
173   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
174
175   if (load_handle_) {
176     DCHECK(web_service_.get());
177     web_service_->CancelRequest(load_handle_);
178   }
179 }
180
181 void SearchProviderInstallData::CallWhenLoaded(const base::Closure& closure) {
182   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
183
184   if (provider_map_.get()) {
185     closure.Run();
186     return;
187   }
188
189   closure_queue_.push_back(closure);
190   if (load_handle_)
191     return;
192
193   if (web_service_.get())
194     load_handle_ = web_service_->GetKeywords(this);
195   else
196     OnLoadFailed();
197 }
198
199 SearchProviderInstallData::State SearchProviderInstallData::GetInstallState(
200     const GURL& requested_origin) {
201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
202   DCHECK(provider_map_.get());
203
204   // First check to see if the origin is the default search provider.
205   if (requested_origin.spec() == default_search_origin_)
206     return INSTALLED_AS_DEFAULT;
207
208   // Is the url any search provider?
209   const TemplateURLSet* urls = provider_map_->GetURLsForHost(
210       requested_origin.host());
211   if (!urls)
212     return NOT_INSTALLED;
213
214   IOThreadSearchTermsData search_terms_data(google_base_url_);
215   for (TemplateURLSet::const_iterator i = urls->begin();
216        i != urls->end(); ++i) {
217     if (IsSameOrigin(requested_origin, *i, search_terms_data))
218       return INSTALLED_BUT_NOT_DEFAULT;
219   }
220   return NOT_INSTALLED;
221 }
222
223 void SearchProviderInstallData::OnGoogleURLChange(
224     const std::string& google_base_url) {
225   google_base_url_ = google_base_url;
226 }
227
228 void SearchProviderInstallData::OnWebDataServiceRequestDone(
229     WebDataService::Handle h,
230     const WDTypedResult* result) {
231   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232
233   // Reset the load_handle so that we don't try and cancel the load in
234   // the destructor.
235   load_handle_ = 0;
236
237   if (!result) {
238     // Results are null if the database went away or (most likely) wasn't
239     // loaded.
240     OnLoadFailed();
241     return;
242   }
243
244   TemplateURL* default_search_provider = NULL;
245   int new_resource_keyword_version = 0;
246   std::vector<TemplateURL*> extracted_template_urls;
247   GetSearchProvidersUsingKeywordResult(*result, NULL, NULL,
248       &extracted_template_urls, &default_search_provider,
249       &new_resource_keyword_version, NULL);
250   template_urls_.get().insert(template_urls_.get().begin(),
251                               extracted_template_urls.begin(),
252                               extracted_template_urls.end());
253   IOThreadSearchTermsData search_terms_data(google_base_url_);
254   provider_map_.reset(new SearchHostToURLsMap());
255   provider_map_->Init(template_urls_.get(), search_terms_data);
256   SetDefault(default_search_provider);
257   NotifyLoaded();
258 }
259
260 void SearchProviderInstallData::SetDefault(const TemplateURL* template_url) {
261   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262
263   if (!template_url) {
264     default_search_origin_.clear();
265     return;
266   }
267
268   DCHECK(template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION);
269
270   IOThreadSearchTermsData search_terms_data(google_base_url_);
271   const GURL url(TemplateURLService::GenerateSearchURLUsingTermsData(
272       template_url, search_terms_data));
273   if (!url.is_valid() || !url.has_host()) {
274     default_search_origin_.clear();
275     return;
276   }
277   default_search_origin_ = url.GetOrigin().spec();
278 }
279
280 void SearchProviderInstallData::OnLoadFailed() {
281   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
282
283   provider_map_.reset(new SearchHostToURLsMap());
284   IOThreadSearchTermsData search_terms_data(google_base_url_);
285   provider_map_->Init(template_urls_.get(), search_terms_data);
286   SetDefault(NULL);
287   NotifyLoaded();
288 }
289
290 void SearchProviderInstallData::NotifyLoaded() {
291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
292
293   std::vector<base::Closure> closure_queue;
294   closure_queue.swap(closure_queue_);
295
296   std::for_each(closure_queue.begin(),
297                 closure_queue.end(),
298                 std::mem_fun_ref(&base::Closure::Run));
299
300   // Since we expect this request to be rare, clear out the information. This
301   // also keeps the responses current as the search providers change.
302   provider_map_.reset();
303   SetDefault(NULL);
304 }