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/prefs/scoped_user_pref_update.h"
     18 #include "base/strings/string16.h"
     19 #include "base/strings/string_number_conversions.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/threading/thread.h"
     22 #include "base/values.h"
     23 #include "chrome/browser/chrome_notification_types.h"
     24 #include "chrome/browser/history/most_visited_tiles_experiment.h"
     25 #include "chrome/browser/history/page_usage_data.h"
     26 #include "chrome/browser/history/top_sites.h"
     27 #include "chrome/browser/profiles/profile.h"
     28 #include "chrome/browser/thumbnails/thumbnail_list_source.h"
     29 #include "chrome/browser/ui/browser.h"
     30 #include "chrome/browser/ui/browser_finder.h"
     31 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     32 #include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
     33 #include "chrome/browser/ui/webui/favicon_source.h"
     34 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
     35 #include "chrome/browser/ui/webui/ntp/ntp_stats.h"
     36 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
     37 #include "chrome/common/pref_names.h"
     38 #include "chrome/common/url_constants.h"
     39 #include "components/pref_registry/pref_registry_syncable.h"
     40 #include "content/public/browser/navigation_controller.h"
     41 #include "content/public/browser/navigation_entry.h"
     42 #include "content/public/browser/notification_source.h"
     43 #include "content/public/browser/url_data_source.h"
     44 #include "content/public/browser/user_metrics.h"
     45 #include "content/public/browser/web_contents.h"
     46 #include "content/public/browser/web_ui.h"
     47 #include "grit/chromium_strings.h"
     48 #include "grit/generated_resources.h"
     49 #include "grit/locale_settings.h"
     50 #include "ui/base/l10n/l10n_util.h"
     51 #include "url/gurl.h"
     52 
     53 using base::UserMetricsAction;
     54 
     55 MostVisitedHandler::MostVisitedHandler()
     56     : got_first_most_visited_request_(false),
     57       most_visited_viewed_(false),
     58       user_action_logged_(false),
     59       weak_ptr_factory_(this) {
     60 }
     61 
     62 MostVisitedHandler::~MostVisitedHandler() {
     63   if (!user_action_logged_ && most_visited_viewed_) {
     64     const GURL ntp_url = GURL(chrome::kChromeUINewTabURL);
     65     int action_id = NTP_FOLLOW_ACTION_OTHER;
     66     content::NavigationEntry* entry =
     67         web_ui()->GetWebContents()->GetController().GetLastCommittedEntry();
     68     if (entry && (entry->GetURL() != ntp_url)) {
     69       action_id =
     70           content::PageTransitionStripQualifier(entry->GetTransitionType());
     71     }
     72 
     73     UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction", action_id,
     74                               NUM_NTP_FOLLOW_ACTIONS);
     75   }
     76 }
     77 
     78 void MostVisitedHandler::RegisterMessages() {
     79   Profile* profile = Profile::FromWebUI(web_ui());
     80   // Set up our sources for thumbnail and favicon data.
     81   content::URLDataSource::Add(profile, new ThumbnailSource(profile, false));
     82   content::URLDataSource::Add(profile, new ThumbnailSource(profile, true));
     83 
     84   // Set up our sources for top-sites data.
     85   content::URLDataSource::Add(profile, new ThumbnailListSource(profile));
     86 
     87   // Register chrome://favicon as a data source for favicons.
     88   content::URLDataSource::Add(
     89       profile, new FaviconSource(profile, FaviconSource::FAVICON));
     90 
     91   history::TopSites* ts = profile->GetTopSites();
     92   if (ts) {
     93     // TopSites updates itself after a delay. This is especially noticable when
     94     // your profile is empty. Ask TopSites to update itself when we're about to
     95     // show the new tab page.
     96     ts->SyncWithHistory();
     97 
     98     // Register for notification when TopSites changes so that we can update
     99     // ourself.
    100     registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
    101                    content::Source<history::TopSites>(ts));
    102   }
    103 
    104   // We pre-emptively make a fetch for the most visited pages so we have the
    105   // results sooner.
    106   StartQueryForMostVisited();
    107 
    108   web_ui()->RegisterMessageCallback("getMostVisited",
    109       base::Bind(&MostVisitedHandler::HandleGetMostVisited,
    110                  base::Unretained(this)));
    111 
    112   // Register ourselves for any most-visited item blacklisting.
    113   web_ui()->RegisterMessageCallback("blacklistURLFromMostVisited",
    114       base::Bind(&MostVisitedHandler::HandleBlacklistUrl,
    115                  base::Unretained(this)));
    116   web_ui()->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
    117       base::Bind(&MostVisitedHandler::HandleRemoveUrlsFromBlacklist,
    118                  base::Unretained(this)));
    119   web_ui()->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
    120       base::Bind(&MostVisitedHandler::HandleClearBlacklist,
    121                  base::Unretained(this)));
    122   web_ui()->RegisterMessageCallback("mostVisitedAction",
    123       base::Bind(&MostVisitedHandler::HandleMostVisitedAction,
    124                  base::Unretained(this)));
    125   web_ui()->RegisterMessageCallback("mostVisitedSelected",
    126       base::Bind(&MostVisitedHandler::HandleMostVisitedSelected,
    127                  base::Unretained(this)));
    128 }
    129 
    130 void MostVisitedHandler::HandleGetMostVisited(const base::ListValue* args) {
    131   if (!got_first_most_visited_request_) {
    132     // If our initial data is already here, return it.
    133     SendPagesValue();
    134     got_first_most_visited_request_ = true;
    135   } else {
    136     StartQueryForMostVisited();
    137   }
    138 }
    139 
    140 void MostVisitedHandler::SendPagesValue() {
    141   if (pages_value_) {
    142     Profile* profile = Profile::FromWebUI(web_ui());
    143     const base::DictionaryValue* url_blacklist =
    144         profile->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist);
    145     bool has_blacklisted_urls = !url_blacklist->empty();
    146     history::TopSites* ts = profile->GetTopSites();
    147     if (ts) {
    148       has_blacklisted_urls = ts->HasBlacklistedItems();
    149 
    150       MaybeRemovePageValues();
    151     }
    152 
    153     base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
    154     web_ui()->CallJavascriptFunction("ntp.setMostVisitedPages",
    155                                      *pages_value_,
    156                                      has_blacklisted_urls_value);
    157     pages_value_.reset();
    158   }
    159 }
    160 
    161 void MostVisitedHandler::StartQueryForMostVisited() {
    162   history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    163   if (ts) {
    164     ts->GetMostVisitedURLs(
    165         base::Bind(&MostVisitedHandler::OnMostVisitedUrlsAvailable,
    166                    weak_ptr_factory_.GetWeakPtr()), false);
    167   }
    168 }
    169 
    170 void MostVisitedHandler::HandleBlacklistUrl(const base::ListValue* args) {
    171   std::string url = base::UTF16ToUTF8(ExtractStringValue(args));
    172   BlacklistUrl(GURL(url));
    173 }
    174 
    175 void MostVisitedHandler::HandleRemoveUrlsFromBlacklist(
    176     const base::ListValue* args) {
    177   DCHECK(args->GetSize() != 0);
    178 
    179   for (base::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 base::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 base::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     base::DictionaryValue* page_value = new base::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   if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
    274     return;
    275 
    276   TabStripModel* tab_strip_model = chrome::FindBrowserWithWebContents(
    277       web_ui()->GetWebContents())->tab_strip_model();
    278   history::TopSites* top_sites = Profile::FromWebUI(web_ui())->GetTopSites();
    279   if (!tab_strip_model || !top_sites) {
    280     NOTREACHED();
    281     return;
    282   }
    283 
    284   std::set<std::string> open_urls;
    285   chrome::GetOpenUrls(*tab_strip_model, *top_sites, &open_urls);
    286   history::MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
    287       open_urls,
    288       pages_value_.get());
    289 }
    290 
    291 // static
    292 void MostVisitedHandler::RegisterProfilePrefs(
    293     user_prefs::PrefRegistrySyncable* registry) {
    294   registry->RegisterDictionaryPref(
    295       prefs::kNtpMostVisitedURLsBlacklist,
    296       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    297 }
    298