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