Upstream version 6.35.121.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 <vector>
8
9 #include "base/logging.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/history/history_notifications.h"
14 #include "chrome/browser/history/most_visited_tiles_experiment.h"
15 #include "chrome/browser/history/top_sites.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search/instant_io_context.h"
18 #include "chrome/browser/search/instant_service_factory.h"
19 #include "chrome/browser/search/instant_service_observer.h"
20 #include "chrome/browser/search/local_ntp_source.h"
21 #include "chrome/browser/search/most_visited_iframe_source.h"
22 #include "chrome/browser/search/search.h"
23 #include "chrome/browser/search/suggestions/suggestions_service.h"
24 #include "chrome/browser/search/suggestions/suggestions_source.h"
25 #include "chrome/browser/search_engines/template_url.h"
26 #include "chrome/browser/search_engines/template_url_service.h"
27 #include "chrome/browser/search_engines/template_url_service_factory.h"
28 #include "chrome/browser/themes/theme_properties.h"
29 #include "chrome/browser/themes/theme_service.h"
30 #include "chrome/browser/themes/theme_service_factory.h"
31 #include "chrome/browser/ui/webui/favicon_source.h"
32 #include "chrome/browser/ui/webui/ntp/thumbnail_list_source.h"
33 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
34 #include "chrome/browser/ui/webui/theme_source.h"
35 #include "chrome/common/pref_names.h"
36 #include "chrome/common/render_messages.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_details.h"
39 #include "content/public/browser/notification_service.h"
40 #include "content/public/browser/notification_source.h"
41 #include "content/public/browser/notification_types.h"
42 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/url_data_source.h"
44 #include "grit/theme_resources.h"
45 #include "net/base/net_util.h"
46 #include "net/url_request/url_request.h"
47 #include "ui/gfx/color_utils.h"
48 #include "ui/gfx/image/image_skia.h"
49 #include "ui/gfx/sys_color_change_listener.h"
50 #include "url/gurl.h"
51
52 using content::BrowserThread;
53
54 namespace {
55
56 const int kSectionBorderAlphaTransparency = 80;
57
58 // Converts SkColor to RGBAColor
59 RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
60   RGBAColor color;
61   color.r = SkColorGetR(sKColor);
62   color.g = SkColorGetG(sKColor);
63   color.b = SkColorGetB(sKColor);
64   color.a = SkColorGetA(sKColor);
65   return color;
66 }
67
68 }  // namespace
69
70 InstantService::InstantService(Profile* profile)
71     : profile_(profile),
72       omnibox_start_margin_(chrome::kDisableStartMargin),
73       weak_ptr_factory_(this) {
74   // Stub for unit tests.
75   if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
76     return;
77
78   ResetInstantSearchPrerenderer();
79
80   registrar_.Add(this,
81                  content::NOTIFICATION_RENDERER_PROCESS_CREATED,
82                  content::NotificationService::AllSources());
83   registrar_.Add(this,
84                  content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
85                  content::NotificationService::AllSources());
86
87   history::TopSites* top_sites = profile_->GetTopSites();
88   if (top_sites) {
89     registrar_.Add(this,
90                    chrome::NOTIFICATION_TOP_SITES_CHANGED,
91                    content::Source<history::TopSites>(top_sites));
92   }
93   instant_io_context_ = new InstantIOContext();
94
95   if (profile_ && profile_->GetResourceContext()) {
96     BrowserThread::PostTask(
97         BrowserThread::IO, FROM_HERE,
98         base::Bind(&InstantIOContext::SetUserDataOnIO,
99                    profile->GetResourceContext(), instant_io_context_));
100   }
101
102   // Set up the data sources that Instant uses on the NTP.
103 #if defined(ENABLE_THEMES)
104   // Listen for theme installation.
105   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
106                  content::Source<ThemeService>(
107                      ThemeServiceFactory::GetForProfile(profile_)));
108
109   content::URLDataSource::Add(profile_, new ThemeSource(profile_));
110 #endif  // defined(ENABLE_THEMES)
111
112   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, false));
113   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, true));
114   content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
115   content::URLDataSource::Add(
116       profile_, new FaviconSource(profile_, FaviconSource::FAVICON));
117   content::URLDataSource::Add(profile_, new LocalNtpSource(profile_));
118   content::URLDataSource::Add(profile_, new MostVisitedIframeSource());
119   if (suggestions::SuggestionsService::IsEnabled()) {
120     content::URLDataSource::Add(
121         profile_, new suggestions::SuggestionsSource(profile_));
122   }
123
124   profile_pref_registrar_.Init(profile_->GetPrefs());
125   profile_pref_registrar_.Add(
126       prefs::kDefaultSearchProviderID,
127       base::Bind(&InstantService::OnDefaultSearchProviderChanged,
128                  base::Unretained(this)));
129
130   registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
131                  content::Source<Profile>(profile_->GetOriginalProfile()));
132 }
133
134 InstantService::~InstantService() {
135 }
136
137 void InstantService::AddInstantProcess(int process_id) {
138   process_ids_.insert(process_id);
139
140   if (instant_io_context_.get()) {
141     BrowserThread::PostTask(BrowserThread::IO,
142                             FROM_HERE,
143                             base::Bind(&InstantIOContext::AddInstantProcessOnIO,
144                                        instant_io_context_,
145                                        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     BrowserThread::PostTask(
203         BrowserThread::IO,
204         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     case chrome::NOTIFICATION_GOOGLE_URL_UPDATED: {
239       OnGoogleURLUpdated(
240           content::Source<Profile>(source).ptr(),
241           content::Details<GoogleURLTracker::UpdatedDetails>(details).ptr());
242       break;
243     }
244     default:
245       NOTREACHED() << "Unexpected notification type in InstantService.";
246   }
247 }
248
249 void InstantService::SendSearchURLsToRenderer(content::RenderProcessHost* rph) {
250   rph->Send(new ChromeViewMsg_SetSearchURLs(
251       chrome::GetSearchURLs(profile_), chrome::GetNewTabPageURL(profile_)));
252 }
253
254 void InstantService::OnOmniboxStartMarginChanged(int start_margin) {
255   omnibox_start_margin_ = start_margin;
256   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
257                     OmniboxStartMarginChanged(omnibox_start_margin_));
258 }
259
260 void InstantService::OnRendererProcessTerminated(int process_id) {
261   process_ids_.erase(process_id);
262
263   if (instant_io_context_.get()) {
264     BrowserThread::PostTask(
265         BrowserThread::IO,
266         FROM_HERE,
267         base::Bind(&InstantIOContext::RemoveInstantProcessOnIO,
268                    instant_io_context_,
269                    process_id));
270   }
271 }
272
273 void InstantService::OnMostVisitedItemsReceived(
274     const history::MostVisitedURLList& data) {
275   history::MostVisitedURLList reordered_data(data);
276   history::MostVisitedTilesExperiment::MaybeShuffle(&reordered_data);
277
278   std::vector<InstantMostVisitedItem> new_most_visited_items;
279   for (size_t i = 0; i < reordered_data.size(); i++) {
280     const history::MostVisitedURL& url = reordered_data[i];
281     InstantMostVisitedItem item;
282     item.url = url.url;
283     item.title = url.title;
284     new_most_visited_items.push_back(item);
285   }
286
287   most_visited_items_ = new_most_visited_items;
288   NotifyAboutMostVisitedItems();
289 }
290
291 void InstantService::NotifyAboutMostVisitedItems() {
292   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
293                     MostVisitedItemsChanged(most_visited_items_));
294 }
295
296 void InstantService::OnThemeChanged(ThemeService* theme_service) {
297   if (!theme_service) {
298     DCHECK(theme_info_.get());
299     FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
300                       ThemeInfoChanged(*theme_info_));
301     return;
302   }
303
304   // Get theme information from theme service.
305   theme_info_.reset(new ThemeBackgroundInfo());
306
307   // Get if the current theme is the default theme.
308   theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
309
310   // Get theme colors.
311   SkColor background_color =
312       theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
313   SkColor text_color =
314       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
315   SkColor link_color =
316       theme_service->GetColor(ThemeProperties::COLOR_NTP_LINK);
317   SkColor text_color_light =
318       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT_LIGHT);
319   SkColor header_color =
320       theme_service->GetColor(ThemeProperties::COLOR_NTP_HEADER);
321   // Generate section border color from the header color.
322   SkColor section_border_color =
323       SkColorSetARGB(kSectionBorderAlphaTransparency,
324                      SkColorGetR(header_color),
325                      SkColorGetG(header_color),
326                      SkColorGetB(header_color));
327
328   // Invert colors if needed.
329   if (gfx::IsInvertedColorScheme()) {
330     background_color = color_utils::InvertColor(background_color);
331     text_color = color_utils::InvertColor(text_color);
332     link_color = color_utils::InvertColor(link_color);
333     text_color_light = color_utils::InvertColor(text_color_light);
334     header_color = color_utils::InvertColor(header_color);
335     section_border_color = color_utils::InvertColor(section_border_color);
336   }
337
338   // Set colors.
339   theme_info_->background_color = SkColorToRGBAColor(background_color);
340   theme_info_->text_color = SkColorToRGBAColor(text_color);
341   theme_info_->link_color = SkColorToRGBAColor(link_color);
342   theme_info_->text_color_light = SkColorToRGBAColor(text_color_light);
343   theme_info_->header_color = SkColorToRGBAColor(header_color);
344   theme_info_->section_border_color = SkColorToRGBAColor(section_border_color);
345
346   int logo_alternate = theme_service->GetDisplayProperty(
347       ThemeProperties::NTP_LOGO_ALTERNATE);
348   theme_info_->logo_alternate = logo_alternate == 1;
349
350   if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
351     // Set theme id for theme background image url.
352     theme_info_->theme_id = theme_service->GetThemeID();
353
354     // Set theme background image horizontal alignment.
355     int alignment = theme_service->GetDisplayProperty(
356         ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
357     if (alignment & ThemeProperties::ALIGN_LEFT)
358       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
359     else if (alignment & ThemeProperties::ALIGN_RIGHT)
360       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT;
361     else
362       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
363
364     // Set theme background image vertical alignment.
365     if (alignment & ThemeProperties::ALIGN_TOP)
366       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP;
367     else if (alignment & ThemeProperties::ALIGN_BOTTOM)
368       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM;
369     else
370       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
371
372     // Set theme backgorund image tiling.
373     int tiling = theme_service->GetDisplayProperty(
374         ThemeProperties::NTP_BACKGROUND_TILING);
375     switch (tiling) {
376       case ThemeProperties::NO_REPEAT:
377         theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
378         break;
379       case ThemeProperties::REPEAT_X:
380         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
381         break;
382       case ThemeProperties::REPEAT_Y:
383         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
384         break;
385       case ThemeProperties::REPEAT:
386         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT;
387         break;
388     }
389
390     // Set theme background image height.
391     gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
392         IDR_THEME_NTP_BACKGROUND);
393     DCHECK(image);
394     theme_info_->image_height = image->height();
395
396     theme_info_->has_attribution =
397        theme_service->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION);
398   }
399
400   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
401                     ThemeInfoChanged(*theme_info_));
402 }
403
404 void InstantService::OnGoogleURLUpdated(
405     Profile* profile,
406     GoogleURLTracker::UpdatedDetails* details) {
407   GURL last_prompted_url(
408       profile->GetPrefs()->GetString(prefs::kLastPromptedGoogleURL));
409
410   // See GoogleURLTracker::OnURLFetchComplete().
411   // last_prompted_url.is_empty() indicates very first run of Chrome. So there
412   // is no need to notify, as there won't be any old state.
413   if (last_prompted_url.is_empty())
414     return;
415
416   ResetInstantSearchPrerenderer();
417
418   // Only the scheme changed. Ignore it since we do not prompt the user in this
419   // case.
420   if (net::StripWWWFromHost(details->first) ==
421       net::StripWWWFromHost(details->second))
422     return;
423
424   FOR_EACH_OBSERVER(InstantServiceObserver, observers_, GoogleURLUpdated());
425 }
426
427 void InstantService::OnDefaultSearchProviderChanged(
428     const std::string& pref_name) {
429   DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID));
430   const TemplateURL* template_url = TemplateURLServiceFactory::GetForProfile(
431       profile_)->GetDefaultSearchProvider();
432   if (!template_url) {
433     // A NULL |template_url| could mean either this notification is sent during
434     // the browser start up operation or the user now has no default search
435     // provider. There is no way for the user to reach this state using the
436     // Chrome settings. Only explicitly poking at the DB or bugs in the Sync
437     // could cause that, neither of which we support.
438     return;
439   }
440
441   ResetInstantSearchPrerenderer();
442
443   FOR_EACH_OBSERVER(
444       InstantServiceObserver, observers_, DefaultSearchProviderChanged());
445 }
446
447 void InstantService::ResetInstantSearchPrerenderer() {
448   if (!chrome::ShouldPrefetchSearchResults())
449     return;
450
451   GURL url(chrome::GetSearchResultPrefetchBaseURL(profile_));
452   if (url.is_valid())
453     instant_prerenderer_.reset(new InstantSearchPrerenderer(profile_, url));
454   else
455     instant_prerenderer_.reset();
456 }