Home | History | Annotate | Download | only in search
      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/strings/string_number_conversions.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/browser/history/history_notifications.h"
     13 #include "chrome/browser/history/most_visited_tiles_experiment.h"
     14 #include "chrome/browser/history/top_sites.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/search/instant_io_context.h"
     17 #include "chrome/browser/search/instant_service_factory.h"
     18 #include "chrome/browser/search/instant_service_observer.h"
     19 #include "chrome/browser/search/local_ntp_source.h"
     20 #include "chrome/browser/search/most_visited_iframe_source.h"
     21 #include "chrome/browser/search/search.h"
     22 #include "chrome/browser/themes/theme_properties.h"
     23 #include "chrome/browser/themes/theme_service.h"
     24 #include "chrome/browser/themes/theme_service_factory.h"
     25 #include "chrome/browser/ui/webui/favicon_source.h"
     26 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
     27 #include "chrome/browser/ui/webui/theme_source.h"
     28 #include "content/public/browser/browser_thread.h"
     29 #include "content/public/browser/notification_service.h"
     30 #include "content/public/browser/notification_types.h"
     31 #include "content/public/browser/render_process_host.h"
     32 #include "content/public/browser/url_data_source.h"
     33 #include "grit/theme_resources.h"
     34 #include "net/url_request/url_request.h"
     35 #include "ui/gfx/color_utils.h"
     36 #include "ui/gfx/image/image_skia.h"
     37 #include "ui/gfx/sys_color_change_listener.h"
     38 #include "url/gurl.h"
     39 
     40 using content::BrowserThread;
     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       ntp_prerenderer_(profile, profile->GetPrefs()),
     61       browser_instant_controller_object_count_(0),
     62       weak_ptr_factory_(this) {
     63   // Stub for unit tests.
     64   if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
     65     return;
     66 
     67   registrar_.Add(this,
     68                  content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     69                  content::NotificationService::AllSources());
     70 
     71   history::TopSites* top_sites = profile_->GetTopSites();
     72   if (top_sites) {
     73     registrar_.Add(this,
     74                    chrome::NOTIFICATION_TOP_SITES_CHANGED,
     75                    content::Source<history::TopSites>(top_sites));
     76   }
     77   instant_io_context_ = new InstantIOContext();
     78 
     79   if (profile_ && profile_->GetResourceContext()) {
     80     BrowserThread::PostTask(
     81         BrowserThread::IO, FROM_HERE,
     82         base::Bind(&InstantIOContext::SetUserDataOnIO,
     83                    profile->GetResourceContext(), instant_io_context_));
     84   }
     85 
     86   // Set up the data sources that Instant uses on the NTP.
     87 #if defined(ENABLE_THEMES)
     88   // Listen for theme installation.
     89   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
     90                  content::Source<ThemeService>(
     91                      ThemeServiceFactory::GetForProfile(profile_)));
     92 
     93   content::URLDataSource::Add(profile, new ThemeSource(profile));
     94 #endif  // defined(ENABLE_THEMES)
     95 
     96   content::URLDataSource::Add(profile, new ThumbnailSource(profile));
     97   content::URLDataSource::Add(profile, new FaviconSource(
     98       profile, FaviconSource::FAVICON));
     99   content::URLDataSource::Add(profile, new LocalNtpSource(profile));
    100   content::URLDataSource::Add(profile, new MostVisitedIframeSource());
    101   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
    102                  content::Source<Profile>(profile_));
    103 }
    104 
    105 InstantService::~InstantService() {
    106 }
    107 
    108 void InstantService::AddInstantProcess(int process_id) {
    109   process_ids_.insert(process_id);
    110 
    111   if (instant_io_context_.get()) {
    112     BrowserThread::PostTask(BrowserThread::IO,
    113                             FROM_HERE,
    114                             base::Bind(&InstantIOContext::AddInstantProcessOnIO,
    115                                        instant_io_context_,
    116                                        process_id));
    117   }
    118 }
    119 
    120 bool InstantService::IsInstantProcess(int process_id) const {
    121   return process_ids_.find(process_id) != process_ids_.end();
    122 }
    123 
    124 void InstantService::AddObserver(InstantServiceObserver* observer) {
    125   observers_.AddObserver(observer);
    126 }
    127 
    128 void InstantService::RemoveObserver(InstantServiceObserver* observer) {
    129   observers_.RemoveObserver(observer);
    130 }
    131 
    132 void InstantService::DeleteMostVisitedItem(const GURL& url) {
    133   history::TopSites* top_sites = profile_->GetTopSites();
    134   if (!top_sites)
    135     return;
    136 
    137   top_sites->AddBlacklistedURL(url);
    138 }
    139 
    140 void InstantService::UndoMostVisitedDeletion(const GURL& url) {
    141   history::TopSites* top_sites = profile_->GetTopSites();
    142   if (!top_sites)
    143     return;
    144 
    145   top_sites->RemoveBlacklistedURL(url);
    146 }
    147 
    148 void InstantService::UndoAllMostVisitedDeletions() {
    149   history::TopSites* top_sites = profile_->GetTopSites();
    150   if (!top_sites)
    151     return;
    152 
    153   top_sites->ClearBlacklistedURLs();
    154 }
    155 
    156 void InstantService::UpdateThemeInfo() {
    157   // Update theme background info.
    158   // Initialize |theme_info| if necessary.
    159   if (!theme_info_)
    160     OnThemeChanged(ThemeServiceFactory::GetForProfile(profile_));
    161   else
    162     OnThemeChanged(NULL);
    163 }
    164 
    165 void InstantService::UpdateMostVisitedItemsInfo() {
    166   NotifyAboutMostVisitedItems();
    167 }
    168 
    169 void InstantService::Shutdown() {
    170   process_ids_.clear();
    171 
    172   if (instant_io_context_.get()) {
    173     BrowserThread::PostTask(
    174         BrowserThread::IO,
    175         FROM_HERE,
    176         base::Bind(&InstantIOContext::ClearInstantProcessesOnIO,
    177                    instant_io_context_));
    178   }
    179   instant_io_context_ = NULL;
    180 }
    181 
    182 scoped_ptr<content::WebContents> InstantService::ReleaseNTPContents() {
    183   return ntp_prerenderer_.ReleaseNTPContents();
    184 }
    185 
    186 content::WebContents* InstantService::GetNTPContents() const {
    187   return ntp_prerenderer_.GetNTPContents();
    188 }
    189 
    190 void InstantService::OnBrowserInstantControllerCreated() {
    191   if (profile_->IsOffTheRecord())
    192     return;
    193 
    194   ++browser_instant_controller_object_count_;
    195 
    196   if (browser_instant_controller_object_count_ == 1)
    197     ntp_prerenderer_.PreloadInstantNTP();
    198 }
    199 
    200 void InstantService::OnBrowserInstantControllerDestroyed() {
    201   if (profile_->IsOffTheRecord())
    202     return;
    203 
    204   DCHECK_GT(browser_instant_controller_object_count_, 0U);
    205   --browser_instant_controller_object_count_;
    206 
    207   // All browser windows have closed, so release the InstantNTP resources to
    208   // work around http://crbug.com/180810.
    209   if (browser_instant_controller_object_count_ == 0)
    210     ntp_prerenderer_.DeleteNTPContents();
    211 }
    212 
    213 void InstantService::Observe(int type,
    214                              const content::NotificationSource& source,
    215                              const content::NotificationDetails& details) {
    216   switch (type) {
    217     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
    218       int process_id =
    219           content::Source<content::RenderProcessHost>(source)->GetID();
    220       process_ids_.erase(process_id);
    221 
    222       if (instant_io_context_.get()) {
    223         BrowserThread::PostTask(
    224             BrowserThread::IO,
    225             FROM_HERE,
    226             base::Bind(&InstantIOContext::RemoveInstantProcessOnIO,
    227                        instant_io_context_,
    228                        process_id));
    229       }
    230       break;
    231     }
    232     case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
    233       history::TopSites* top_sites = profile_->GetTopSites();
    234       if (top_sites) {
    235         top_sites->GetMostVisitedURLs(
    236             base::Bind(&InstantService::OnMostVisitedItemsReceived,
    237                        weak_ptr_factory_.GetWeakPtr()));
    238       }
    239       break;
    240     }
    241 #if defined(ENABLE_THEMES)
    242     case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: {
    243       OnThemeChanged(content::Source<ThemeService>(source).ptr());
    244       break;
    245     }
    246 #endif  // defined(ENABLE_THEMES)
    247     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
    248       // Last chance to delete InstantNTP contents. We generally delete
    249       // preloaded InstantNTP when the last BrowserInstantController object is
    250       // destroyed. When the browser shutdown happens without closing browsers,
    251       // there is a race condition between BrowserInstantController destruction
    252       // and Profile destruction.
    253       if (GetNTPContents())
    254         ntp_prerenderer_.DeleteNTPContents();
    255       break;
    256     }
    257     default:
    258       NOTREACHED() << "Unexpected notification type in InstantService.";
    259   }
    260 }
    261 
    262 void InstantService::OnMostVisitedItemsReceived(
    263     const history::MostVisitedURLList& data) {
    264   history::MostVisitedURLList reordered_data(data);
    265   history::MostVisitedTilesExperiment::MaybeShuffle(&reordered_data);
    266 
    267   std::vector<InstantMostVisitedItem> new_most_visited_items;
    268   for (size_t i = 0; i < reordered_data.size(); i++) {
    269     const history::MostVisitedURL& url = reordered_data[i];
    270     InstantMostVisitedItem item;
    271     item.url = url.url;
    272     item.title = url.title;
    273     new_most_visited_items.push_back(item);
    274   }
    275 
    276   most_visited_items_ = new_most_visited_items;
    277   NotifyAboutMostVisitedItems();
    278 }
    279 
    280 void InstantService::NotifyAboutMostVisitedItems() {
    281   FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
    282                     MostVisitedItemsChanged(most_visited_items_));
    283 }
    284 
    285 void InstantService::OnThemeChanged(ThemeService* theme_service) {
    286   if (!theme_service) {
    287     DCHECK(theme_info_.get());
    288     FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
    289                       ThemeInfoChanged(*theme_info_));
    290     return;
    291   }
    292 
    293   // Get theme information from theme service.
    294   theme_info_.reset(new ThemeBackgroundInfo());
    295 
    296   // Get if the current theme is the default theme.
    297   theme_info_->using_default_theme = theme_service->UsingDefaultTheme();
    298 
    299   // Get theme colors.
    300   SkColor background_color =
    301       theme_service->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
    302   SkColor text_color =
    303       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT);
    304   SkColor link_color =
    305       theme_service->GetColor(ThemeProperties::COLOR_NTP_LINK);
    306   SkColor text_color_light =
    307       theme_service->GetColor(ThemeProperties::COLOR_NTP_TEXT_LIGHT);
    308   SkColor header_color =
    309       theme_service->GetColor(ThemeProperties::COLOR_NTP_HEADER);
    310   // Generate section border color from the header color.
    311   SkColor section_border_color =
    312       SkColorSetARGB(kSectionBorderAlphaTransparency,
    313                      SkColorGetR(header_color),
    314                      SkColorGetG(header_color),
    315                      SkColorGetB(header_color));
    316 
    317   // Invert colors if needed.
    318   if (gfx::IsInvertedColorScheme()) {
    319     background_color = color_utils::InvertColor(background_color);
    320     text_color = color_utils::InvertColor(text_color);
    321     link_color = color_utils::InvertColor(link_color);
    322     text_color_light = color_utils::InvertColor(text_color_light);
    323     header_color = color_utils::InvertColor(header_color);
    324     section_border_color = color_utils::InvertColor(section_border_color);
    325   }
    326 
    327   // Set colors.
    328   theme_info_->background_color = SkColorToRGBAColor(background_color);
    329   theme_info_->text_color = SkColorToRGBAColor(text_color);
    330   theme_info_->link_color = SkColorToRGBAColor(link_color);
    331   theme_info_->text_color_light = SkColorToRGBAColor(text_color_light);
    332   theme_info_->header_color = SkColorToRGBAColor(header_color);
    333   theme_info_->section_border_color = SkColorToRGBAColor(section_border_color);
    334 
    335   // Set logo for the theme. By default, use alternate logo.
    336   theme_info_->logo_alternate = true;
    337   int logo_alternate = 0;
    338   if (theme_service->GetDisplayProperty(
    339       ThemeProperties::NTP_LOGO_ALTERNATE, &logo_alternate))
    340     theme_info_->logo_alternate = logo_alternate == 1;
    341 
    342   if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
    343     // Set theme id for theme background image url.
    344     theme_info_->theme_id = theme_service->GetThemeID();
    345 
    346     // Set theme background image horizontal alignment.
    347     int alignment = 0;
    348     theme_service->GetDisplayProperty(
    349         ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &alignment);
    350     if (alignment & ThemeProperties::ALIGN_LEFT)
    351       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
    352     else if (alignment & ThemeProperties::ALIGN_RIGHT)
    353       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT;
    354     else
    355       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
    356 
    357     // Set theme background image vertical alignment.
    358     if (alignment & ThemeProperties::ALIGN_TOP)
    359       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP;
    360     else if (alignment & ThemeProperties::ALIGN_BOTTOM)
    361       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM;
    362     else
    363       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
    364 
    365     // Set theme backgorund image tiling.
    366     int tiling = 0;
    367     theme_service->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_TILING,
    368                                       &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 InstantNTPPrerenderer* InstantService::ntp_prerenderer() {
    399   return &ntp_prerenderer_;
    400 }
    401