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"
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"
38 #if !defined(OS_ANDROID)
39 #include "chrome/browser/search/local_ntp_source.h"
44 const int kSectionBorderAlphaTransparency = 80;
46 // Converts SkColor to RGBAColor
47 RGBAColor SkColorToRGBAColor(const SkColor& sKColor) {
49 color.r = SkColorGetR(sKColor);
50 color.g = SkColorGetG(sKColor);
51 color.b = SkColorGetB(sKColor);
52 color.a = SkColorGetA(sKColor);
58 InstantService::InstantService(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))
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();
72 previous_google_base_url_ =
73 GURL(UIThreadSearchTermsData(profile).GoogleBaseURLValue());
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()));
86 ResetInstantSearchPrerenderer();
89 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
90 content::NotificationService::AllSources());
92 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
93 content::NotificationService::AllSources());
95 history::TopSites* top_sites = profile_->GetTopSites();
98 chrome::NOTIFICATION_TOP_SITES_CHANGED,
99 content::Source<history::TopSites>(top_sites));
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_));
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_)));
116 content::URLDataSource::Add(profile_, new ThemeSource(profile_));
117 #endif // defined(ENABLE_THEMES)
119 // TODO(aurimas) remove this #if once instant_service.cc is no longer compiled
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)
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_));
135 InstantService::~InstantService() {
136 if (template_url_service_)
137 template_url_service_->RemoveObserver(this);
140 void InstantService::AddInstantProcess(int process_id) {
141 process_ids_.insert(process_id);
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));
151 bool InstantService::IsInstantProcess(int process_id) const {
152 return process_ids_.find(process_id) != process_ids_.end();
155 void InstantService::AddObserver(InstantServiceObserver* observer) {
156 observers_.AddObserver(observer);
159 void InstantService::RemoveObserver(InstantServiceObserver* observer) {
160 observers_.RemoveObserver(observer);
163 void InstantService::DeleteMostVisitedItem(const GURL& url) {
164 history::TopSites* top_sites = profile_->GetTopSites();
168 top_sites->AddBlacklistedURL(url);
171 void InstantService::UndoMostVisitedDeletion(const GURL& url) {
172 history::TopSites* top_sites = profile_->GetTopSites();
176 top_sites->RemoveBlacklistedURL(url);
179 void InstantService::UndoAllMostVisitedDeletions() {
180 history::TopSites* top_sites = profile_->GetTopSites();
184 top_sites->ClearBlacklistedURLs();
187 void InstantService::UpdateThemeInfo() {
188 // Update theme background info.
189 // Initialize |theme_info| if necessary.
191 OnThemeChanged(ThemeServiceFactory::GetForProfile(profile_));
193 OnThemeChanged(NULL);
196 void InstantService::UpdateMostVisitedItemsInfo() {
197 NotifyAboutMostVisitedItems();
200 void InstantService::Shutdown() {
201 process_ids_.clear();
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_));
209 instant_io_context_ = NULL;
212 void InstantService::Observe(int type,
213 const content::NotificationSource& source,
214 const content::NotificationDetails& details) {
216 case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
217 SendSearchURLsToRenderer(
218 content::Source<content::RenderProcessHost>(source).ptr());
220 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
221 OnRendererProcessTerminated(
222 content::Source<content::RenderProcessHost>(source)->GetID());
224 case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
225 history::TopSites* top_sites = profile_->GetTopSites();
227 top_sites->GetMostVisitedURLs(
228 base::Bind(&InstantService::OnMostVisitedItemsReceived,
229 weak_ptr_factory_.GetWeakPtr()), false);
233 #if defined(ENABLE_THEMES)
234 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
235 OnThemeChanged(content::Source<ThemeService>(source).ptr());
238 #endif // defined(ENABLE_THEMES)
240 NOTREACHED() << "Unexpected notification type in InstantService.";
244 void InstantService::SendSearchURLsToRenderer(content::RenderProcessHost* rph) {
245 rph->Send(new ChromeViewMsg_SetSearchURLs(
246 chrome::GetSearchURLs(profile_), chrome::GetNewTabPageURL(profile_)));
249 void InstantService::OnOmniboxStartMarginChanged(int start_margin) {
250 omnibox_start_margin_ = start_margin;
251 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
252 OmniboxStartMarginChanged(omnibox_start_margin_));
255 void InstantService::OnRendererProcessTerminated(int process_id) {
256 process_ids_.erase(process_id);
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));
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;
274 item.title = url.title;
275 new_most_visited_items.push_back(item);
278 most_visited_items_ = new_most_visited_items;
279 NotifyAboutMostVisitedItems();
282 void InstantService::NotifyAboutMostVisitedItems() {
283 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
284 MostVisitedItemsChanged(most_visited_items_));
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_));
295 // Get theme information from theme service.
296 theme_info_.reset(new ThemeBackgroundInfo());
298 // Get if the current theme is the default theme.
299 theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
302 SkColor background_color =
303 theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
305 theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
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));
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);
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);
337 int logo_alternate = theme_service->GetDisplayProperty(
338 ThemeProperties::NTP_LOGO_ALTERNATE);
339 theme_info_->logo_alternate = logo_alternate == 1;
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();
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;
353 theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
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;
361 theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
363 // Set theme backgorund image tiling.
364 int tiling = theme_service->GetDisplayProperty(
365 ThemeProperties::NTP_BACKGROUND_TILING);
367 case ThemeProperties::NO_REPEAT:
368 theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
370 case ThemeProperties::REPEAT_X:
371 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X;
373 case ThemeProperties::REPEAT_Y:
374 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y;
376 case ThemeProperties::REPEAT:
377 theme_info_->image_tiling = THEME_BKGRND_IMAGE_REPEAT;
381 // Set theme background image height.
382 gfx::ImageSkia* image = theme_service->GetImageSkiaNamed(
383 IDR_THEME_NTP_BACKGROUND);
385 theme_info_->image_height = image->height();
387 theme_info_->has_attribution =
388 theme_service->HasCustomImage(IDR_THEME_NTP_ATTRIBUTION);
391 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
392 ThemeInfoChanged(*theme_info_));
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);
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;
419 if (default_search_provider_changed) {
420 ResetInstantSearchPrerenderer();
421 FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
422 DefaultSearchProviderChanged());
426 void InstantService::ResetInstantSearchPrerenderer() {
427 if (!chrome::ShouldPrefetchSearchResults())
430 GURL url(chrome::GetSearchResultPrefetchBaseURL(profile_));
431 instant_prerenderer_.reset(
432 url.is_valid() ? new InstantSearchPrerenderer(profile_, url) : NULL);