Upstream version 5.34.104.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_engines/template_url.h"
24 #include "chrome/browser/search_engines/template_url_service.h"
25 #include "chrome/browser/search_engines/template_url_service_factory.h"
26 #include "chrome/browser/themes/theme_properties.h"
27 #include "chrome/browser/themes/theme_service.h"
28 #include "chrome/browser/themes/theme_service_factory.h"
29 #include "chrome/browser/ui/webui/favicon_source.h"
30 #include "chrome/browser/ui/webui/ntp/thumbnail_list_source.h"
31 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
32 #include "chrome/browser/ui/webui/theme_source.h"
33 #include "chrome/common/pref_names.h"
34 #include "chrome/common/render_messages.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_details.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/notification_types.h"
40 #include "content/public/browser/render_process_host.h"
41 #include "content/public/browser/url_data_source.h"
42 #include "grit/theme_resources.h"
43 #include "net/base/net_util.h"
44 #include "net/url_request/url_request.h"
45 #include "ui/gfx/color_utils.h"
46 #include "ui/gfx/image/image_skia.h"
47 #include "ui/gfx/sys_color_change_listener.h"
48 #include "url/gurl.h"
49
50 using content::BrowserThread;
51
52 namespace {
53
54 const int kSectionBorderAlphaTransparency = 80;
55
56 // Converts SkColor to RGBAColor
57 RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
58   RGBAColor color;
59   color.r = SkColorGetR(sKColor);
60   color.g = SkColorGetG(sKColor);
61   color.b = SkColorGetB(sKColor);
62   color.a = SkColorGetA(sKColor);
63   return color;
64 }
65
66 }  // namespace
67
68 InstantService::InstantService(Profile* profile)
69     : profile_(profile),
70       omnibox_start_margin_(chrome::kDisableStartMargin),
71       weak_ptr_factory_(this) {
72   // Stub for unit tests.
73   if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
74     return;
75
76   ResetInstantSearchPrerenderer();
77
78   registrar_.Add(this,
79                  content::NOTIFICATION_RENDERER_PROCESS_CREATED,
80                  content::NotificationService::AllSources());
81   registrar_.Add(this,
82                  content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
83                  content::NotificationService::AllSources());
84
85   history::TopSites* top_sites = profile_->GetTopSites();
86   if (top_sites) {
87     registrar_.Add(this,
88                    chrome::NOTIFICATION_TOP_SITES_CHANGED,
89                    content::Source<history::TopSites>(top_sites));
90   }
91   instant_io_context_ = new InstantIOContext();
92
93   if (profile_ && profile_->GetResourceContext()) {
94     BrowserThread::PostTask(
95         BrowserThread::IO, FROM_HERE,
96         base::Bind(&InstantIOContext::SetUserDataOnIO,
97                    profile->GetResourceContext(), instant_io_context_));
98   }
99
100   // Set up the data sources that Instant uses on the NTP.
101 #if defined(ENABLE_THEMES)
102   // Listen for theme installation.
103   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
104                  content::Source<ThemeService>(
105                      ThemeServiceFactory::GetForProfile(profile_)));
106
107   content::URLDataSource::Add(profile_, new ThemeSource(profile_));
108 #endif  // defined(ENABLE_THEMES)
109
110   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, false));
111   content::URLDataSource::Add(profile_, new ThumbnailSource(profile_, true));
112   content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
113   content::URLDataSource::Add(
114       profile_, new FaviconSource(profile_, FaviconSource::FAVICON));
115   content::URLDataSource::Add(profile_, new LocalNtpSource(profile_));
116   content::URLDataSource::Add(profile_, new MostVisitedIframeSource());
117
118   profile_pref_registrar_.Init(profile_->GetPrefs());
119   profile_pref_registrar_.Add(
120       prefs::kDefaultSearchProviderID,
121       base::Bind(&InstantService::OnDefaultSearchProviderChanged,
122                  base::Unretained(this)));
123
124   registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
125                  content::Source<Profile>(profile_->GetOriginalProfile()));
126 }
127
128 InstantService::~InstantService() {
129 }
130
131 void InstantService::AddInstantProcess(int process_id) {
132   process_ids_.insert(process_id);
133
134   if (instant_io_context_.get()) {
135     BrowserThread::PostTask(BrowserThread::IO,
136                             FROM_HERE,
137                             base::Bind(&InstantIOContext::AddInstantProcessOnIO,
138                                        instant_io_context_,
139                                        process_id));
140   }
141 }
142
143 bool InstantService::IsInstantProcess(int process_id) const {
144   return process_ids_.find(process_id) != process_ids_.end();
145 }
146
147 void InstantService::AddObserver(InstantServiceObserver* observer) {
148   observers_.AddObserver(observer);
149 }
150
151 void InstantService::RemoveObserver(InstantServiceObserver* observer) {
152   observers_.RemoveObserver(observer);
153 }
154
155 void InstantService::DeleteMostVisitedItem(const GURL& url) {
156   history::TopSites* top_sites = profile_->GetTopSites();
157   if (!top_sites)
158     return;
159
160   top_sites->AddBlacklistedURL(url);
161 }
162
163 void InstantService::UndoMostVisitedDeletion(const GURL& url) {
164   history::TopSites* top_sites = profile_->GetTopSites();
165   if (!top_sites)
166     return;
167
168   top_sites->RemoveBlacklistedURL(url);
169 }
170
171 void InstantService::UndoAllMostVisitedDeletions() {
172   history::TopSites* top_sites = profile_->GetTopSites();
173   if (!top_sites)
174     return;
175
176   top_sites->ClearBlacklistedURLs();
177 }
178
179 void InstantService::UpdateThemeInfo() {
180   // Update theme background info.
181   // Initialize |theme_info| if necessary.
182   if (!theme_info_)
183     OnThemeChanged(ThemeServiceFactory::GetForProfile(profile_));
184   else
185     OnThemeChanged(NULL);
186 }
187
188 void InstantService::UpdateMostVisitedItemsInfo() {
189   NotifyAboutMostVisitedItems();
190 }
191
192 void InstantService::Shutdown() {
193   process_ids_.clear();
194
195   if (instant_io_context_.get()) {
196     BrowserThread::PostTask(
197         BrowserThread::IO,
198         FROM_HERE,
199         base::Bind(&InstantIOContext::ClearInstantProcessesOnIO,
200                    instant_io_context_));
201   }
202   instant_io_context_ = NULL;
203 }
204
205 void InstantService::Observe(int type,
206                              const content::NotificationSource& source,
207                              const content::NotificationDetails& details) {
208   switch (type) {
209     case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
210       SendSearchURLsToRenderer(
211           content::Source<content::RenderProcessHost>(source).ptr());
212       break;
213     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
214       OnRendererProcessTerminated(
215           content::Source<content::RenderProcessHost>(source)->GetID());
216       break;
217     case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
218       history::TopSites* top_sites = profile_->GetTopSites();
219       if (top_sites) {
220         top_sites->GetMostVisitedURLs(
221             base::Bind(&InstantService::OnMostVisitedItemsReceived,
222                        weak_ptr_factory_.GetWeakPtr()), false);
223       }
224       break;
225     }
226 #if defined(ENABLE_THEMES)
227     case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
228       OnThemeChanged(content::Source<ThemeService>(source).ptr());
229       break;
230     }
231 #endif  // defined(ENABLE_THEMES)
232     case chrome::NOTIFICATION_GOOGLE_URL_UPDATED: {
233       OnGoogleURLUpdated(
234           content::Source<Profile>(source).ptr(),
235           content::Details<GoogleURLTracker::UpdatedDetails>(details).ptr());
236       break;
237     }
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     BrowserThread::PostTask(
259         BrowserThread::IO,
260         FROM_HERE,
261         base::Bind(&InstantIOContext::RemoveInstantProcessOnIO,
262                    instant_io_context_,
263                    process_id));
264   }
265 }
266
267 void InstantService::OnMostVisitedItemsReceived(
268     const history::MostVisitedURLList& data) {
269   history::MostVisitedURLList reordered_data(data);
270   history::MostVisitedTilesExperiment::MaybeShuffle(&reordered_data);
271
272   std::vector<InstantMostVisitedItem> new_most_visited_items;
273   for (size_t i = 0; i < reordered_data.size(); i++) {
274     const history::MostVisitedURL& url = reordered_data[i];
275     InstantMostVisitedItem item;
276     item.url = url.url;
277     item.title = url.title;
278     new_most_visited_items.push_back(item);
279   }
280
281   most_visited_items_ = new_most_visited_items;
282   NotifyAboutMostVisitedItems();
283 }
284
285 void InstantService::NotifyAboutMostVisitedItems() {
286   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
287                     MostVisitedItemsChanged(most_visited_items_));
288 }
289
290 void InstantService::OnThemeChanged(ThemeService* theme_service) {
291   if (!theme_service) {
292     DCHECK(theme_info_.get());
293     FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
294                       ThemeInfoChanged(*theme_info_));
295     return;
296   }
297
298   // Get theme information from theme service.
299   theme_info_.reset(new ThemeBackgroundInfo());
300
301   // Get if the current theme is the default theme.
302   theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
303
304   // Get theme colors.
305   SkColor background_color =
306       theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
307   SkColor text_color =
308       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
309   SkColor link_color =
310       theme_service->GetColor(ThemeProperties::COLOR_NTP_LINK);
311   SkColor text_color_light =
312       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT_LIGHT);
313   SkColor header_color =
314       theme_service->GetColor(ThemeProperties::COLOR_NTP_HEADER);
315   // Generate section border color from the header color.
316   SkColor section_border_color =
317       SkColorSetARGB(kSectionBorderAlphaTransparency,
318                      SkColorGetR(header_color),
319                      SkColorGetG(header_color),
320                      SkColorGetB(header_color));
321
322   // Invert colors if needed.
323   if (gfx::IsInvertedColorScheme()) {
324     background_color = color_utils::InvertColor(background_color);
325     text_color = color_utils::InvertColor(text_color);
326     link_color = color_utils::InvertColor(link_color);
327     text_color_light = color_utils::InvertColor(text_color_light);
328     header_color = color_utils::InvertColor(header_color);
329     section_border_color = color_utils::InvertColor(section_border_color);
330   }
331
332   // Set colors.
333   theme_info_->background_color = SkColorToRGBAColor(background_color);
334   theme_info_->text_color = SkColorToRGBAColor(text_color);
335   theme_info_->link_color = SkColorToRGBAColor(link_color);
336   theme_info_->text_color_light = SkColorToRGBAColor(text_color_light);
337   theme_info_->header_color = SkColorToRGBAColor(header_color);
338   theme_info_->section_border_color = SkColorToRGBAColor(section_border_color);
339
340   int logo_alternate = theme_service->GetDisplayProperty(
341       ThemeProperties::NTP_LOGO_ALTERNATE);
342   theme_info_->logo_alternate = logo_alternate == 1;
343
344   if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
345     // Set theme id for theme background image url.
346     theme_info_->theme_id = theme_service->GetThemeID();
347
348     // Set theme background image horizontal alignment.
349     int alignment = theme_service->GetDisplayProperty(
350         ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
351     if (alignment & ThemeProperties::ALIGN_LEFT)
352       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
353     else if (alignment & ThemeProperties::ALIGN_RIGHT)
354       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT;
355     else
356       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
357
358     // Set theme background image vertical alignment.
359     if (alignment & ThemeProperties::ALIGN_TOP)
360       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP;
361     else if (alignment & ThemeProperties::ALIGN_BOTTOM)
362       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM;
363     else
364       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
365
366     // Set theme backgorund image tiling.
367     int tiling = theme_service->GetDisplayProperty(
368         ThemeProperties::NTP_BACKGROUND_TILING);
369     switch (tiling) {
370       case ThemeProperties::NO_REPEAT:
371         theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
372         break;
373       case ThemeProperties::REPEAT_X:
374         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
375         break;
376       case ThemeProperties::REPEAT_Y:
377         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
378         break;
379       case ThemeProperties::REPEAT:
380         theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT;
381         break;
382     }
383
384     // Set theme background image height.
385     gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
386         IDR_THEME_NTP_BACKGROUND);
387     DCHECK(image);
388     theme_info_->image_height = image->height();
389
390     theme_info_->has_attribution =
391        theme_service->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION);
392   }
393
394   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
395                     ThemeInfoChanged(*theme_info_));
396 }
397
398 void InstantService::OnGoogleURLUpdated(
399     Profile* profile,
400     GoogleURLTracker::UpdatedDetails* details) {
401   GURL last_prompted_url(
402       profile->GetPrefs()->GetString(prefs::kLastPromptedGoogleURL));
403
404   // See GoogleURLTracker::OnURLFetchComplete().
405   // last_prompted_url.is_empty() indicates very first run of Chrome. So there
406   // is no need to notify, as there won't be any old state.
407   if (last_prompted_url.is_empty())
408     return;
409
410   ResetInstantSearchPrerenderer();
411
412   // Only the scheme changed. Ignore it since we do not prompt the user in this
413   // case.
414   if (net::StripWWWFromHost(details->first) ==
415       net::StripWWWFromHost(details->second))
416     return;
417
418   FOR_EACH_OBSERVER(InstantServiceObserver, observers_, GoogleURLUpdated());
419 }
420
421 void InstantService::OnDefaultSearchProviderChanged(
422     const std::string& pref_name) {
423   DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID));
424   const TemplateURL* template_url = TemplateURLServiceFactory::GetForProfile(
425       profile_)->GetDefaultSearchProvider();
426   if (!template_url) {
427     // A NULL |template_url| could mean either this notification is sent during
428     // the browser start up operation or the user now has no default search
429     // provider. There is no way for the user to reach this state using the
430     // Chrome settings. Only explicitly poking at the DB or bugs in the Sync
431     // could cause that, neither of which we support.
432     return;
433   }
434
435   ResetInstantSearchPrerenderer();
436
437   FOR_EACH_OBSERVER(
438       InstantServiceObserver, observers_, DefaultSearchProviderChanged());
439 }
440
441 void InstantService::ResetInstantSearchPrerenderer() {
442   if (!chrome::ShouldPrefetchSearchResults())
443     return;
444
445   GURL url(chrome::GetSearchResultPrefetchBaseURL(profile_));
446   if (url.is_valid())
447     instant_prerenderer_.reset(new InstantSearchPrerenderer(profile_, url));
448   else
449     instant_prerenderer_.reset();
450 }