Home | History | Annotate | Download | only in ntp
      1 // Copyright (c) 2012 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/ui/webui/ntp/most_visited_handler.h"
      6 
      7 #include <set>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/command_line.h"
     12 #include "base/md5.h"
     13 #include "base/memory/scoped_vector.h"
     14 #include "base/memory/singleton.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/prefs/pref_service.h"
     17 #include "base/strings/string16.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/threading/thread.h"
     21 #include "base/values.h"
     22 #include "chrome/browser/chrome_notification_types.h"
     23 #include "chrome/browser/history/most_visited_tiles_experiment.h"
     24 #include "chrome/browser/history/page_usage_data.h"
     25 #include "chrome/browser/history/top_sites.h"
     26 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     27 #include "chrome/browser/profiles/profile.h"
     28 #include "chrome/browser/ui/browser.h"
     29 #include "chrome/browser/ui/browser_finder.h"
     30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     31 #include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
     32 #include "chrome/browser/ui/webui/favicon_source.h"
     33 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
     34 #include "chrome/browser/ui/webui/ntp/ntp_stats.h"
     35 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
     36 #include "chrome/common/pref_names.h"
     37 #include "chrome/common/url_constants.h"
     38 #include "components/user_prefs/pref_registry_syncable.h"
     39 #include "content/public/browser/navigation_controller.h"
     40 #include "content/public/browser/navigation_entry.h"
     41 #include "content/public/browser/notification_source.h"
     42 #include "content/public/browser/url_data_source.h"
     43 #include "content/public/browser/user_metrics.h"
     44 #include "content/public/browser/web_contents.h"
     45 #include "content/public/browser/web_ui.h"
     46 #include "grit/chromium_strings.h"
     47 #include "grit/generated_resources.h"
     48 #include "grit/locale_settings.h"
     49 #include "ui/base/l10n/l10n_util.h"
     50 #include "url/gurl.h"
     51 
     52 using content::UserMetricsAction;
     53 
     54 MostVisitedHandler::MostVisitedHandler()
     55     : weak_ptr_factory_(this),
     56       got_first_most_visited_request_(false),
     57       most_visited_viewed_(false),
     58       user_action_logged_(false) {
     59 }
     60 
     61 MostVisitedHandler::~MostVisitedHandler() {
     62   if (!user_action_logged_ && most_visited_viewed_) {
     63     const GURL ntp_url = GURL(chrome::kChromeUINewTabURL);
     64     int action_id = NTP_FOLLOW_ACTION_OTHER;
     65     content::NavigationEntry* entry =
     66         web_ui()->GetWebContents()->GetController().GetActiveEntry();
     67     if (entry && (entry->GetURL() != ntp_url)) {
     68       action_id =
     69           content::PageTransitionStripQualifier(entry->GetTransitionType());
     70     }
     71 
     72     UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction", action_id,
     73                               NUM_NTP_FOLLOW_ACTIONS);
     74   }
     75 }
     76 
     77 void MostVisitedHandler::RegisterMessages() {
     78   Profile* profile = Profile::FromWebUI(web_ui());
     79   // Set up our sources for thumbnail and favicon data.
     80   ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
     81   content::URLDataSource::Add(profile, thumbnail_src);
     82 
     83 #if defined(OS_ANDROID)
     84   // Register chrome://touch-icon as a data source for touch icons or favicons.
     85   content::URLDataSource::Add(profile,
     86                               new FaviconSource(profile, FaviconSource::ANY));
     87 #endif
     88   // Register chrome://favicon as a data source for favicons.
     89   content::URLDataSource::Add(
     90       profile, new FaviconSource(profile, FaviconSource::FAVICON));
     91 
     92   history::TopSites* ts = profile->GetTopSites();
     93   if (ts) {
     94     // TopSites updates itself after a delay. This is especially noticable when
     95     // your profile is empty. Ask TopSites to update itself when we're about to
     96     // show the new tab page.
     97     ts->SyncWithHistory();
     98 
     99     // Register for notification when TopSites changes so that we can update
    100     // ourself.
    101     registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
    102                    content::Source<history::TopSites>(ts));
    103   }
    104 
    105   // We pre-emptively make a fetch for the most visited pages so we have the
    106   // results sooner.
    107   StartQueryForMostVisited();
    108 
    109   web_ui()->RegisterMessageCallback("getMostVisited",
    110       base::Bind(&MostVisitedHandler::HandleGetMostVisited,
    111                  base::Unretained(this)));
    112 
    113   // Register ourselves for any most-visited item blacklisting.
    114   web_ui()->RegisterMessageCallback("blacklistURLFromMostVisited",
    115       base::Bind(&MostVisitedHandler::HandleBlacklistUrl,
    116                  base::Unretained(this)));
    117   web_ui()->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
    118       base::Bind(&MostVisitedHandler::HandleRemoveUrlsFromBlacklist,
    119                  base::Unretained(this)));
    120   web_ui()->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
    121       base::Bind(&MostVisitedHandler::HandleClearBlacklist,
    122                  base::Unretained(this)));
    123   web_ui()->RegisterMessageCallback("mostVisitedAction",
    124       base::Bind(&MostVisitedHandler::HandleMostVisitedAction,
    125                  base::Unretained(this)));
    126   web_ui()->RegisterMessageCallback("mostVisitedSelected",
    127       base::Bind(&MostVisitedHandler::HandleMostVisitedSelected,
    128                  base::Unretained(this)));
    129 }
    130 
    131 void MostVisitedHandler::HandleGetMostVisited(const ListValue* args) {
    132   if (!got_first_most_visited_request_) {
    133     // If our initial data is already here, return it.
    134     SendPagesValue();
    135     got_first_most_visited_request_ = true;
    136   } else {
    137     StartQueryForMostVisited();
    138   }
    139 }
    140 
    141 void MostVisitedHandler::SendPagesValue() {
    142   if (pages_value_) {
    143     Profile* profile = Profile::FromWebUI(web_ui());
    144     const DictionaryValue* url_blacklist =
    145         profile->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist);
    146     bool has_blacklisted_urls = !url_blacklist->empty();
    147     history::TopSites* ts = profile->GetTopSites();
    148     if (ts) {
    149       has_blacklisted_urls = ts->HasBlacklistedItems();
    150 
    151       MaybeRemovePageValues();
    152     }
    153 
    154     base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
    155     web_ui()->CallJavascriptFunction("ntp.setMostVisitedPages",
    156                                      *pages_value_,
    157                                      has_blacklisted_urls_value);
    158     pages_value_.reset();
    159   }
    160 }
    161 
    162 void MostVisitedHandler::StartQueryForMostVisited() {
    163   history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    164   if (ts) {
    165     ts->GetMostVisitedURLs(
    166         base::Bind(&MostVisitedHandler::OnMostVisitedUrlsAvailable,
    167                    weak_ptr_factory_.GetWeakPtr()));
    168   }
    169 }
    170 
    171 void MostVisitedHandler::HandleBlacklistUrl(const ListValue* args) {
    172   std::string url = UTF16ToUTF8(ExtractStringValue(args));
    173   BlacklistUrl(GURL(url));
    174 }
    175 
    176 void MostVisitedHandler::HandleRemoveUrlsFromBlacklist(const ListValue* args) {
    177   DCHECK(args->GetSize() != 0);
    178 
    179   for (ListValue::const_iterator iter = args->begin();
    180        iter != args->end(); ++iter) {
    181     std::string url;
    182     bool r = (*iter)->GetAsString(&url);
    183     if (!r) {
    184       NOTREACHED();
    185       return;
    186     }
    187     content::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"));
    188     history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    189     if (ts)
    190       ts->RemoveBlacklistedURL(GURL(url));
    191   }
    192 }
    193 
    194 void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) {
    195   content::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"));
    196 
    197   history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    198   if (ts)
    199     ts->ClearBlacklistedURLs();
    200 }
    201 
    202 void MostVisitedHandler::HandleMostVisitedAction(const base::ListValue* args) {
    203   DCHECK(args);
    204 
    205   double action_id;
    206   if (!args->GetDouble(0, &action_id))
    207     NOTREACHED();
    208 
    209   UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction",
    210                             static_cast<int>(action_id),
    211                             NUM_NTP_FOLLOW_ACTIONS);
    212   most_visited_viewed_ = true;
    213   user_action_logged_ = true;
    214 }
    215 
    216 void MostVisitedHandler::HandleMostVisitedSelected(
    217     const base::ListValue* args) {
    218   most_visited_viewed_ = true;
    219 }
    220 
    221 void MostVisitedHandler::SetPagesValueFromTopSites(
    222     const history::MostVisitedURLList& data) {
    223   pages_value_.reset(new ListValue);
    224 
    225   history::MostVisitedURLList top_sites(data);
    226   history::MostVisitedTilesExperiment::MaybeShuffle(&top_sites);
    227 
    228   for (size_t i = 0; i < top_sites.size(); i++) {
    229     const history::MostVisitedURL& url = top_sites[i];
    230     DictionaryValue* page_value = new DictionaryValue();
    231     if (url.url.is_empty()) {
    232       page_value->SetBoolean("filler", true);
    233       pages_value_->Append(page_value);
    234       continue;
    235     }
    236 
    237     NewTabUI::SetUrlTitleAndDirection(page_value,
    238                                       url.title,
    239                                       url.url);
    240     pages_value_->Append(page_value);
    241   }
    242 }
    243 
    244 void MostVisitedHandler::OnMostVisitedUrlsAvailable(
    245     const history::MostVisitedURLList& data) {
    246   SetPagesValueFromTopSites(data);
    247   if (got_first_most_visited_request_) {
    248     SendPagesValue();
    249   }
    250 }
    251 
    252 void MostVisitedHandler::Observe(int type,
    253                                  const content::NotificationSource& source,
    254                                  const content::NotificationDetails& details) {
    255   DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
    256 
    257   // Most visited urls changed, query again.
    258   StartQueryForMostVisited();
    259 }
    260 
    261 void MostVisitedHandler::BlacklistUrl(const GURL& url) {
    262   history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    263   if (ts)
    264     ts->AddBlacklistedURL(url);
    265   content::RecordAction(UserMetricsAction("MostVisited_UrlBlacklisted"));
    266 }
    267 
    268 std::string MostVisitedHandler::GetDictionaryKeyForUrl(const std::string& url) {
    269   return base::MD5String(url);
    270 }
    271 
    272 void MostVisitedHandler::MaybeRemovePageValues() {
    273 // The code below uses APIs not available on Android and the experiment should
    274 // not run there.
    275 #if !defined(OS_ANDROID)
    276   if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
    277     return;
    278 
    279   TabStripModel* tab_strip_model = chrome::FindBrowserWithWebContents(
    280       web_ui()->GetWebContents())->tab_strip_model();
    281   history::TopSites* top_sites = Profile::FromWebUI(web_ui())->GetTopSites();
    282   if (!tab_strip_model || !top_sites) {
    283     NOTREACHED();
    284     return;
    285   }
    286 
    287   std::set<std::string> open_urls;
    288   chrome::GetOpenUrls(*tab_strip_model, *top_sites, &open_urls);
    289   history::MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
    290       open_urls,
    291       pages_value_.get());
    292 #endif
    293 }
    294 
    295 // static
    296 void MostVisitedHandler::RegisterProfilePrefs(
    297     user_prefs::PrefRegistrySyncable* registry) {
    298   registry->RegisterDictionaryPref(
    299       prefs::kNtpMostVisitedURLsBlacklist,
    300       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    301 }
    302