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.
5 #include "chrome/browser/search/instant_service.h"
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"
50 using content::BrowserThread;
54 const int kSectionBorderAlphaTransparency = 80;
56 // Converts SkColor to RGBAColor
57 RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
59 color.r = SkColorGetR(sKColor);
60 color.g = SkColorGetG(sKColor);
61 color.b = SkColorGetB(sKColor);
62 color.a = SkColorGetA(sKColor);
68 InstantService::InstantService(Profile* profile)
70 omnibox_start_margin_(chrome::kDisableStartMargin),
71 weak_ptr_factory_(this) {
72 // Stub for unit tests.
73 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
76 ResetInstantSearchPrerenderer();
79 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
80 content::NotificationService::AllSources());
82 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
83 content::NotificationService::AllSources());
85 history::TopSites* top_sites = profile_->GetTopSites();
88 chrome::NOTIFICATION_TOP_SITES_CHANGED,
89 content::Source<history::TopSites>(top_sites));
91 instant_io_context_ = new InstantIOContext();
93 if (profile_ && profile_->GetResourceContext()) {
94 BrowserThread::PostTask(
95 BrowserThread::IO, FROM_HERE,
96 base::Bind(&InstantIOContext::SetUserDataOnIO,
97 profile->GetResourceContext(), instant_io_context_));
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_)));
107 content::URLDataSource::Add(profile_, new ThemeSource(profile_));
108 #endif // defined(ENABLE_THEMES)
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());
118 profile_pref_registrar_.Init(profile_->GetPrefs());
119 profile_pref_registrar_.Add(
120 prefs::kDefaultSearchProviderID,
121 base::Bind(&InstantService::OnDefaultSearchProviderChanged,
122 base::Unretained(this)));
124 registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_URL_UPDATED,
125 content::Source<Profile>(profile_->GetOriginalProfile()));
128 InstantService::~InstantService() {
131 void InstantService::AddInstantProcess(int process_id) {
132 process_ids_.insert(process_id);
134 if (instant_io_context_.get()) {
135 BrowserThread::PostTask(BrowserThread::IO,
137 base::Bind(&InstantIOContext::AddInstantProcessOnIO,
143 bool InstantService::IsInstantProcess(int process_id) const {
144 return process_ids_.find(process_id) != process_ids_.end();
147 void InstantService::AddObserver(InstantServiceObserver* observer) {
148 observers_.AddObserver(observer);
151 void InstantService::RemoveObserver(InstantServiceObserver* observer) {
152 observers_.RemoveObserver(observer);
155 void InstantService::DeleteMostVisitedItem(const GURL& url) {
156 history::TopSites* top_sites = profile_->GetTopSites();
160 top_sites->AddBlacklistedURL(url);
163 void InstantService::UndoMostVisitedDeletion(const GURL& url) {
164 history::TopSites* top_sites = profile_->GetTopSites();
168 top_sites->RemoveBlacklistedURL(url);
171 void InstantService::UndoAllMostVisitedDeletions() {
172 history::TopSites* top_sites = profile_->GetTopSites();
176 top_sites->ClearBlacklistedURLs();
179 void InstantService::UpdateThemeInfo() {
180 // Update theme background info.
181 // Initialize |theme_info| if necessary.
183 OnThemeChanged(ThemeServiceFactory::GetForProfile(profile_));
185 OnThemeChanged(NULL);
188 void InstantService::UpdateMostVisitedItemsInfo() {
189 NotifyAboutMostVisitedItems();
192 void InstantService::Shutdown() {
193 process_ids_.clear();
195 if (instant_io_context_.get()) {
196 BrowserThread::PostTask(
199 base::Bind(&InstantIOContext::ClearInstantProcessesOnIO,
200 instant_io_context_));
202 instant_io_context_ = NULL;
205 void InstantService::Observe(int type,
206 const content::NotificationSource& source,
207 const content::NotificationDetails& details) {
209 case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
210 SendSearchURLsToRenderer(
211 content::Source<content::RenderProcessHost>(source).ptr());
213 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
214 OnRendererProcessTerminated(
215 content::Source<content::RenderProcessHost>(source)->GetID());
217 case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
218 history::TopSites* top_sites = profile_->GetTopSites();
220 top_sites->GetMostVisitedURLs(
221 base::Bind(&InstantService::OnMostVisitedItemsReceived,
222 weak_ptr_factory_.GetWeakPtr()), false);
226 #if defined(ENABLE_THEMES)
227 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
228 OnThemeChanged(content::Source<ThemeService>(source).ptr());
231 #endif // defined(ENABLE_THEMES)
232 case chrome::NOTIFICATION_GOOGLE_URL_UPDATED: {
234 content::Source<Profile>(source).ptr(),
235 content::Details<GoogleURLTracker::UpdatedDetails>(details).ptr());
239 NOTREACHED() << "Unexpected notification type in InstantService.";
243 void InstantService::SendSearchURLsToRenderer(content::RenderProcessHost* rph) {
244 rph->Send(new ChromeViewMsg_SetSearchURLs(
245 chrome::GetSearchURLs(profile_), chrome::GetNewTabPageURL(profile_)));
248 void InstantService::OnOmniboxStartMarginChanged(int start_margin) {
249 omnibox_start_margin_ = start_margin;
250 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
251 OmniboxStartMarginChanged(omnibox_start_margin_));
254 void InstantService::OnRendererProcessTerminated(int process_id) {
255 process_ids_.erase(process_id);
257 if (instant_io_context_.get()) {
258 BrowserThread::PostTask(
261 base::Bind(&InstantIOContext::RemoveInstantProcessOnIO,
267 void InstantService::OnMostVisitedItemsReceived(
268 const history::MostVisitedURLList& data) {
269 history::MostVisitedURLList reordered_data(data);
270 history::MostVisitedTilesExperiment::MaybeShuffle(&reordered_data);
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;
277 item.title = url.title;
278 new_most_visited_items.push_back(item);
281 most_visited_items_ = new_most_visited_items;
282 NotifyAboutMostVisitedItems();
285 void InstantService::NotifyAboutMostVisitedItems() {
286 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
287 MostVisitedItemsChanged(most_visited_items_));
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_));
298 // Get theme information from theme service.
299 theme_info_.reset(new ThemeBackgroundInfo());
301 // Get if the current theme is the default theme.
302 theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
305 SkColor background_color =
306 theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
308 theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
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));
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);
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);
340 int logo_alternate = theme_service->GetDisplayProperty(
341 ThemeProperties::NTP_LOGO_ALTERNATE);
342 theme_info_->logo_alternate = logo_alternate == 1;
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();
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;
356 theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
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;
364 theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
366 // Set theme backgorund image tiling.
367 int tiling = theme_service->GetDisplayProperty(
368 ThemeProperties::NTP_BACKGROUND_TILING);
370 case ThemeProperties::NO_REPEAT:
371 theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
373 case ThemeProperties::REPEAT_X:
374 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
376 case ThemeProperties::REPEAT_Y:
377 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
379 case ThemeProperties::REPEAT:
380 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT;
384 // Set theme background image height.
385 gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
386 IDR_THEME_NTP_BACKGROUND);
388 theme_info_->image_height = image->height();
390 theme_info_->has_attribution =
391 theme_service->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION);
394 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
395 ThemeInfoChanged(*theme_info_));
398 void InstantService::OnGoogleURLUpdated(
400 GoogleURLTracker::UpdatedDetails* details) {
401 GURL last_prompted_url(
402 profile->GetPrefs()->GetString(prefs::kLastPromptedGoogleURL));
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())
410 ResetInstantSearchPrerenderer();
412 // Only the scheme changed. Ignore it since we do not prompt the user in this
414 if (net::StripWWWFromHost(details->first) ==
415 net::StripWWWFromHost(details->second))
418 FOR_EACH_OBSERVER(InstantServiceObserver, observers_, GoogleURLUpdated());
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();
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.
435 ResetInstantSearchPrerenderer();
438 InstantServiceObserver, observers_, DefaultSearchProviderChanged());
441 void InstantService::ResetInstantSearchPrerenderer() {
442 if (!chrome::ShouldPrefetchSearchResults())
445 GURL url(chrome::GetSearchResultPrefetchBaseURL(profile_));
447 instant_prerenderer_.reset(new InstantSearchPrerenderer(profile_, url));
449 instant_prerenderer_.reset();