Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / search / instant_service.cc
1 // Copyright 2013 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/instant_service.h"
6
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/history/most_visited_tiles_experiment.h"
9 #include "chrome/browser/history/top_sites.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/search/instant_io_context.h"
12 #include "chrome/browser/search/instant_service_observer.h"
13 #include "chrome/browser/search/local_ntp_source.h"
14 #include "chrome/browser/search/most_visited_iframe_source.h"
15 #include "chrome/browser/search/search.h"
16 #include "chrome/browser/search/suggestions/suggestions_source.h"
17 #include "chrome/browser/search_engines/search_terms_data.h"
18 #include "chrome/browser/search_engines/template_url_service.h"
19 #include "chrome/browser/search_engines/template_url_service_factory.h"
20 #include "chrome/browser/themes/theme_properties.h"
21 #include "chrome/browser/themes/theme_service.h"
22 #include "chrome/browser/themes/theme_service_factory.h"
23 #include "chrome/browser/thumbnails/thumbnail_list_source.h"
24 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
25 #include "chrome/browser/ui/webui/favicon_source.h"
26 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
27 #include "chrome/browser/ui/webui/theme_source.h"
28 #include "chrome/common/render_messages.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_types.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/url_data_source.h"
34 #include "grit/theme_resources.h"
35 #include "third_party/skia/include/core/SkColor.h"
36 #include "ui/gfx/color_utils.h"
37 #include "ui/gfx/image/image_skia.h"
38 #include "ui/gfx/sys_color_change_listener.h"
39
40
41 namespace {
42
43 const int kSectionBorderAlphaTransparency = 80;
44
45 // Converts SkColor to RGBAColor
46 RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
47   RGBAColor color;
48   color.r = SkColorGetR(sKColor);
49   color.g = SkColorGetG(sKColor);
50   color.b = SkColorGetB(sKColor);
51   color.a = SkColorGetA(sKColor);
52   return color;
53 }
54
55 }  // namespace
56
57 InstantService::InstantService(Profile* profile)
58     : profile_(profile),
59       template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)),
60       omnibox_start_margin_(chrome::kDisableStartMargin),
61       weak_ptr_factory_(this) {
62   // The initialization below depends on a typical set of browser threads. Skip
63   // it if we are running in a unit test without the full suite.
64   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI))
65     return;
66
67   // This depends on the existence of the typical browser threads. Therefore it
68   // is only instantiated here (after the check for a UI thread above).
69   instant_io_context_ = new InstantIOContext();
70
71   previous_google_base_url_ =
72       GURL(UIThreadSearchTermsData(profile).GoogleBaseURLValue());
73
74   // TemplateURLService is NULL by default in tests.
75   if (template_url_service_) {
76     template_url_service_->AddObserver(this);
77     const TemplateURL* default_search_provider =
78         template_url_service_->GetDefaultSearchProvider();
79     if (default_search_provider) {
80       previous_default_search_provider_.reset(
81           new TemplateURLData(default_search_provider->data()));
82     }
83   }
84
85   ResetInstantSearchPrerenderer();
86
87   registrar_.Add(this,
88                  content::NOTIFICATION_RENDERER_PROCESS_CREATED,
89                  content::NotificationService::AllSources());
90   registrar_.Add(this,
91                  content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
92                  content::NotificationService::AllSources());
93
94   history::TopSites* top_sites = profile_->GetTopSites();
95   if (top_sites) {
96     registrar_.Add(this,
97                    chrome::NOTIFICATION_TOP_SITES_CHANGED,
98                    content::Source<history::TopSites>(top_sites));
99   }
100
101   if (profile_ && profile_->GetResourceContext()) {
102     content::BrowserThread::PostTask(
103         content::BrowserThread::IO, FROM_HERE,
104         base::Bind(&InstantIOContext::SetUserDataOnIO,
105                    profile->GetResourceContext(), instant_io_context_));
106   }
107
108   // Set up the data sources that Instant uses on the NTP.
109 #if defined(ENABLE_THEMES)
110   // Listen for theme installation.
111   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
112                  content::Source<ThemeService>(
113                      ThemeServiceFactory::GetForProfile(profile_)));
114
115   content::URLDataSource::Add(profile_, new ThemeSource(profile_));
116 #endif  // defined(ENABLE_THEMES)
117
118   // TODO(aurimas) remove this #if once instant_service.cc is no longer compiled
119   // on Android.
120 #if !defined(OS_ANDROID)
121   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, false));
122   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, true));
123   content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
124 #endif  // !defined(OS_ANDROID)
125
126   content::URLDataSource::Add(
127       profile_, new FaviconSource(profile_, FaviconSource::FAVICON));
128   content::URLDataSource::Add(profile_, new LocalNtpSource(profile_));
129   content::URLDataSource::Add(profile_, new MostVisitedIframeSource());
130   content::URLDataSource::Add(
131       profile_, new suggestions::SuggestionsSource(profile_));
132 }
133
134 InstantService::~InstantService() {
135   if (template_url_service_)
136     template_url_service_->RemoveObserver(this);
137 }
138
139 void InstantService::AddInstantProcess(int process_id) {
140   process_ids_.insert(process_id);
141
142   if (instant_io_context_.get()) {
143     content::BrowserThread::PostTask(
144         content::BrowserThread::IO, FROM_HERE,
145         base::Bind(&InstantIOContext::AddInstantProcessOnIO,
146                    instant_io_context_, process_id));
147   }
148 }
149
150 bool InstantService::IsInstantProcess(int process_id) const {
151   return process_ids_.find(process_id) != process_ids_.end();
152 }
153
154 void InstantService::AddObserver(InstantServiceObserver* observer) {
155   observers_.AddObserver(observer);
156 }
157
158 void InstantService::RemoveObserver(InstantServiceObserver* observer) {
159   observers_.RemoveObserver(observer);
160 }
161
162 void InstantService::DeleteMostVisitedItem(const GURL& url) {
163   history::TopSites* top_sites = profile_->GetTopSites();
164   if (!top_sites)
165     return;
166
167   top_sites->AddBlacklistedURL(url);
168 }
169
170 void InstantService::UndoMostVisitedDeletion(const GURL& url) {
171   history::TopSites* top_sites = profile_->GetTopSites();
172   if (!top_sites)
173     return;
174
175   top_sites->RemoveBlacklistedURL(url);
176 }
177
178 void InstantService::UndoAllMostVisitedDeletions() {
179   history::TopSites* top_sites = profile_->GetTopSites();
180   if (!top_sites)
181     return;
182
183   top_sites->ClearBlacklistedURLs();
184 }
185
186 void InstantService::UpdateThemeInfo() {
187   // Update theme background info.
188   // Initialize |theme_info| if necessary.
189   if (!theme_info_)
190     OnThemeChanged(ThemeServiceFactory::GetForProfile(profile_));
191   else
192     OnThemeChanged(NULL);
193 }
194
195 void InstantService::UpdateMostVisitedItemsInfo() {
196   NotifyAboutMostVisitedItems();
197 }
198
199 void InstantService::Shutdown() {
200   process_ids_.clear();
201
202   if (instant_io_context_.get()) {
203     content::BrowserThread::PostTask(
204         content::BrowserThread::IO, FROM_HERE,
205         base::Bind(&InstantIOContext::ClearInstantProcessesOnIO,
206                    instant_io_context_));
207   }
208   instant_io_context_ = NULL;
209 }
210
211 void InstantService::Observe(int type,
212                              const content::NotificationSource& source,
213                              const content::NotificationDetails& details) {
214   switch (type) {
215     case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
216       SendSearchURLsToRenderer(
217           content::Source<content::RenderProcessHost>(source).ptr());
218       break;
219     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
220       OnRendererProcessTerminated(
221           content::Source<content::RenderProcessHost>(source)->GetID());
222       break;
223     case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
224       history::TopSites* top_sites = profile_->GetTopSites();
225       if (top_sites) {
226         top_sites->GetMostVisitedURLs(
227             base::Bind(&InstantService::OnMostVisitedItemsReceived,
228                        weak_ptr_factory_.GetWeakPtr()), false);
229       }
230       break;
231     }
232 #if defined(ENABLE_THEMES)
233     case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
234       OnThemeChanged(content::Source<ThemeService>(source).ptr());
235       break;
236     }
237 #endif  // defined(ENABLE_THEMES)
238     default:
239       NOTREACHED() << "Unexpected notification type in InstantService.";
240   }
241 }
242
243 void InstantService::SendSearchURLsToRenderer(content::RenderProcessHost* rph) {
244   rph->Send(new ChromeViewMsg_SetSearchURLs(
245       chrome::GetSearchURLs(profile_), chrome::GetNewTabPageURL(profile_)));
246 }
247
248 void InstantService::OnOmniboxStartMarginChanged(int start_margin) {
249   omnibox_start_margin_ = start_margin;
250   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
251                     OmniboxStartMarginChanged(omnibox_start_margin_));
252 }
253
254 void InstantService::OnRendererProcessTerminated(int process_id) {
255   process_ids_.erase(process_id);
256
257   if (instant_io_context_.get()) {
258     content::BrowserThread::PostTask(
259         content::BrowserThread::IO, FROM_HERE,
260         base::Bind(&InstantIOContext::RemoveInstantProcessOnIO,
261                    instant_io_context_, process_id));
262   }
263 }
264
265 void InstantService::OnMostVisitedItemsReceived(
266     const history::MostVisitedURLList& data) {
267   history::MostVisitedURLList reordered_data(data);
268   history::MostVisitedTilesExperiment::MaybeShuffle(&reordered_data);
269
270   std::vector<InstantMostVisitedItem> new_most_visited_items;
271   for (size_t i = 0; i < reordered_data.size(); i++) {
272     const history::MostVisitedURL& url = reordered_data[i];
273     InstantMostVisitedItem item;
274     item.url = url.url;
275     item.title = url.title;
276     new_most_visited_items.push_back(item);
277   }
278
279   most_visited_items_ = new_most_visited_items;
280   NotifyAboutMostVisitedItems();
281 }
282
283 void InstantService::NotifyAboutMostVisitedItems() {
284   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
285                     MostVisitedItemsChanged(most_visited_items_));
286 }
287
288 void InstantService::OnThemeChanged(ThemeService* theme_service) {
289   if (!theme_service) {
290     DCHECK(theme_info_.get());
291     FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
292                       ThemeInfoChanged(*theme_info_));
293     return;
294   }
295
296   // Get theme information from theme service.
297   theme_info_.reset(new ThemeBackgroundInfo());
298
299   // Get if the current theme is the default theme.
300   theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
301
302   // Get theme colors.
303   SkColor background_color =
304       theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
305   SkColor text_color =
306       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
307   SkColor link_color =
308       theme_service->GetColor(ThemeProperties::COLOR_NTP_LINK);
309   SkColor text_color_light =
310       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT_LIGHT);
311   SkColor header_color =
312       theme_service->GetColor(ThemeProperties::COLOR_NTP_HEADER);
313   // Generate section border color from the header color.
314   SkColor section_border_color =
315       SkColorSetARGB(kSectionBorderAlphaTransparency,
316                      SkColorGetR(header_color),
317                      SkColorGetG(header_color),
318                      SkColorGetB(header_color));
319
320   // Invert colors if needed.
321   if (gfx::IsInvertedColorScheme()) {
322     background_color = color_utils::InvertColor(background_color);
323     text_color = color_utils::InvertColor(text_color);
324     link_color = color_utils::InvertColor(link_color);
325     text_color_light = color_utils::InvertColor(text_color_light);
326     header_color = color_utils::InvertColor(header_color);
327     section_border_color = color_utils::InvertColor(section_border_color);
328   }
329
330   // Set colors.
331   theme_info_->background_color = SkColorToRGBAColor(background_color);
332   theme_info_->text_color = SkColorToRGBAColor(text_color);
333   theme_info_->link_color = SkColorToRGBAColor(link_color);
334   theme_info_->text_color_light = SkColorToRGBAColor(text_color_light);
335   theme_info_->header_color = SkColorToRGBAColor(header_color);
336   theme_info_->section_border_color = SkColorToRGBAColor(section_border_color);
337
338   int logo_alternate = theme_service->GetDisplayProperty(
339       ThemeProperties::NTP_LOGO_ALTERNATE);
340   theme_info_->logo_alternate = logo_alternate == 1;
341
342   if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
343     // Set theme id for theme background image url.
344     theme_info_->theme_id = theme_service->GetThemeID();
345
346     // Set theme background image horizontal alignment.
347     int alignment = theme_service->GetDisplayProperty(
348         ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
349     if (alignment & ThemeProperties::ALIGN_LEFT)
350       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
351     else if (alignment & ThemeProperties::ALIGN_RIGHT)
352       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT;
353     else
354       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
355
356     // Set theme background image vertical alignment.
357     if (alignment & ThemeProperties::ALIGN_TOP)
358       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP;
359     else if (alignment & ThemeProperties::ALIGN_BOTTOM)
360       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM;
361     else
362       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
363
364     // Set theme backgorund image tiling.
365     int tiling = theme_service->GetDisplayProperty(
366         ThemeProperties::NTP_BACKGROUND_TILING);
367     switch (tiling) {
368       case ThemeProperties::NO_REPEAT:
369         theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
370         break;
371       case ThemeProperties::REPEAT_X:
372         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
373         break;
374       case ThemeProperties::REPEAT_Y:
375         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
376         break;
377       case ThemeProperties::REPEAT:
378         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT;
379         break;
380     }
381
382     // Set theme background image height.
383     gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
384         IDR_THEME_NTP_BACKGROUND);
385     DCHECK(image);
386     theme_info_->image_height = image->height();
387
388     theme_info_->has_attribution =
389        theme_service->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION);
390   }
391
392   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
393                     ThemeInfoChanged(*theme_info_));
394 }
395
396 void InstantService::OnTemplateURLServiceChanged() {
397   // Check whether the default search provider was changed.
398   const TemplateURL* template_url =
399       template_url_service_->GetDefaultSearchProvider();
400   bool default_search_provider_changed = !TemplateURL::MatchesData(
401       template_url, previous_default_search_provider_.get());
402   if (default_search_provider_changed) {
403     previous_default_search_provider_.reset(
404         template_url ? new TemplateURLData(template_url->data()) : NULL);
405   }
406
407   // Note that, even if the TemplateURL for the Default Search Provider has not
408   // changed, the effective URLs might change if they reference the Google base
409   // URL. The TemplateURLService will notify us when the effective URL changes
410   // in this way but it's up to us to do the work to check both.
411   GURL google_base_url(UIThreadSearchTermsData(profile_).GoogleBaseURLValue());
412   if (google_base_url != previous_google_base_url_) {
413     previous_google_base_url_ = google_base_url;
414     if (template_url && template_url->HasGoogleBaseURLs())
415       default_search_provider_changed = true;
416   }
417
418   if (default_search_provider_changed) {
419     ResetInstantSearchPrerenderer();
420     FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
421                       DefaultSearchProviderChanged());
422   }
423 }
424
425 void InstantService::ResetInstantSearchPrerenderer() {
426   if (!chrome::ShouldPrefetchSearchResults())
427     return;
428
429   GURL url(chrome::GetSearchResultPrefetchBaseURL(profile_));
430   instant_prerenderer_.reset(
431       url.is_valid() ? new InstantSearchPrerenderer(profile_, url) : NULL);
432 }