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