Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2011 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/most_visited_handler.h"
      6 
      7 #include <set>
      8 
      9 #include "base/callback.h"
     10 #include "base/command_line.h"
     11 #include "base/md5.h"
     12 #include "base/memory/scoped_vector.h"
     13 #include "base/memory/singleton.h"
     14 #include "base/string16.h"
     15 #include "base/string_number_conversions.h"
     16 #include "base/threading/thread.h"
     17 #include "base/utf_string_conversions.h"
     18 #include "base/values.h"
     19 #include "chrome/browser/history/page_usage_data.h"
     20 #include "chrome/browser/history/top_sites.h"
     21 #include "chrome/browser/metrics/user_metrics.h"
     22 #include "chrome/browser/prefs/pref_service.h"
     23 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
     26 #include "chrome/browser/ui/webui/favicon_source.h"
     27 #include "chrome/browser/ui/webui/new_tab_ui.h"
     28 #include "chrome/browser/ui/webui/thumbnail_source.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "content/browser/browser_thread.h"
     32 #include "content/common/notification_source.h"
     33 #include "content/common/notification_type.h"
     34 #include "googleurl/src/gurl.h"
     35 #include "grit/chromium_strings.h"
     36 #include "grit/generated_resources.h"
     37 #include "grit/locale_settings.h"
     38 #include "ui/base/l10n/l10n_util.h"
     39 
     40 namespace {
     41 
     42 // The number of most visited pages we show.
     43 const size_t kMostVisitedPages = 8;
     44 
     45 // The number of days of history we consider for most visited entries.
     46 const int kMostVisitedScope = 90;
     47 
     48 }  // namespace
     49 
     50 // This struct is used when getting the pre-populated pages in case the user
     51 // hasn't filled up his most visited pages.
     52 struct MostVisitedHandler::MostVisitedPage {
     53   string16 title;
     54   GURL url;
     55   GURL thumbnail_url;
     56   GURL favicon_url;
     57 };
     58 
     59 MostVisitedHandler::MostVisitedHandler()
     60     : got_first_most_visited_request_(false) {
     61 }
     62 
     63 MostVisitedHandler::~MostVisitedHandler() {
     64 }
     65 
     66 WebUIMessageHandler* MostVisitedHandler::Attach(WebUI* web_ui) {
     67   Profile* profile = web_ui->GetProfile();
     68   // Set up our sources for thumbnail and favicon data.
     69   ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
     70   profile->GetChromeURLDataManager()->AddDataSource(thumbnail_src);
     71 
     72   profile->GetChromeURLDataManager()->AddDataSource(new FaviconSource(profile));
     73 
     74   // Get notifications when history is cleared.
     75   registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
     76                  Source<Profile>(profile));
     77 
     78   WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
     79 
     80   // We pre-emptively make a fetch for the most visited pages so we have the
     81   // results sooner.
     82   StartQueryForMostVisited();
     83   return result;
     84 }
     85 
     86 void MostVisitedHandler::RegisterMessages() {
     87   // Register ourselves as the handler for the "mostvisited" message from
     88   // Javascript.
     89   web_ui_->RegisterMessageCallback("getMostVisited",
     90       NewCallback(this, &MostVisitedHandler::HandleGetMostVisited));
     91 
     92   // Register ourselves for any most-visited item blacklisting.
     93   web_ui_->RegisterMessageCallback("blacklistURLFromMostVisited",
     94       NewCallback(this, &MostVisitedHandler::HandleBlacklistURL));
     95   web_ui_->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
     96       NewCallback(this, &MostVisitedHandler::HandleRemoveURLsFromBlacklist));
     97   web_ui_->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
     98       NewCallback(this, &MostVisitedHandler::HandleClearBlacklist));
     99 
    100   // Register ourself for pinned URL messages.
    101   web_ui_->RegisterMessageCallback("addPinnedURL",
    102       NewCallback(this, &MostVisitedHandler::HandleAddPinnedURL));
    103   web_ui_->RegisterMessageCallback("removePinnedURL",
    104       NewCallback(this, &MostVisitedHandler::HandleRemovePinnedURL));
    105 }
    106 
    107 void MostVisitedHandler::HandleGetMostVisited(const ListValue* args) {
    108   if (!got_first_most_visited_request_) {
    109     // If our intial data is already here, return it.
    110     SendPagesValue();
    111     got_first_most_visited_request_ = true;
    112   } else {
    113     StartQueryForMostVisited();
    114   }
    115 }
    116 
    117 void MostVisitedHandler::SendPagesValue() {
    118   if (pages_value_.get()) {
    119     Profile* profile = web_ui_->GetProfile();
    120     const DictionaryValue* url_blacklist =
    121         profile->GetPrefs()->GetDictionary(prefs::kNTPMostVisitedURLsBlacklist);
    122     bool has_blacklisted_urls = !url_blacklist->empty();
    123     history::TopSites* ts = profile->GetTopSites();
    124     if (ts)
    125       has_blacklisted_urls = ts->HasBlacklistedItems();
    126     FundamentalValue first_run(IsFirstRun());
    127     FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
    128     web_ui_->CallJavascriptFunction("mostVisitedPages",
    129                                     *(pages_value_.get()),
    130                                     first_run,
    131                                     has_blacklisted_urls_value);
    132     pages_value_.reset();
    133   }
    134 }
    135 
    136 void MostVisitedHandler::StartQueryForMostVisited() {
    137   // Use TopSites.
    138   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    139   if (ts) {
    140     ts->GetMostVisitedURLs(
    141         &topsites_consumer_,
    142         NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable));
    143   }
    144 }
    145 
    146 void MostVisitedHandler::HandleBlacklistURL(const ListValue* args) {
    147   std::string url = UTF16ToUTF8(ExtractStringValue(args));
    148   BlacklistURL(GURL(url));
    149 }
    150 
    151 void MostVisitedHandler::HandleRemoveURLsFromBlacklist(const ListValue* args) {
    152   DCHECK(args->GetSize() != 0);
    153 
    154   for (ListValue::const_iterator iter = args->begin();
    155        iter != args->end(); ++iter) {
    156     std::string url;
    157     bool r = (*iter)->GetAsString(&url);
    158     if (!r) {
    159       NOTREACHED();
    160       return;
    161     }
    162     UserMetrics::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"),
    163                               web_ui_->GetProfile());
    164     history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    165     if (ts)
    166       ts->RemoveBlacklistedURL(GURL(url));
    167   }
    168 }
    169 
    170 void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) {
    171   UserMetrics::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"),
    172                             web_ui_->GetProfile());
    173 
    174   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    175   if (ts)
    176     ts->ClearBlacklistedURLs();
    177 }
    178 
    179 void MostVisitedHandler::HandleAddPinnedURL(const ListValue* args) {
    180   DCHECK_EQ(5U, args->GetSize()) << "Wrong number of params to addPinnedURL";
    181   MostVisitedPage mvp;
    182   std::string tmp_string;
    183   string16 tmp_string16;
    184   int index;
    185 
    186   bool r = args->GetString(0, &tmp_string);
    187   DCHECK(r) << "Missing URL in addPinnedURL from the NTP Most Visited.";
    188   mvp.url = GURL(tmp_string);
    189 
    190   r = args->GetString(1, &tmp_string16);
    191   DCHECK(r) << "Missing title in addPinnedURL from the NTP Most Visited.";
    192   mvp.title = tmp_string16;
    193 
    194   r = args->GetString(2, &tmp_string);
    195   DCHECK(r) << "Failed to read the favicon URL in addPinnedURL from the NTP "
    196             << "Most Visited.";
    197   if (!tmp_string.empty())
    198     mvp.favicon_url = GURL(tmp_string);
    199 
    200   r = args->GetString(3, &tmp_string);
    201   DCHECK(r) << "Failed to read the thumbnail URL in addPinnedURL from the NTP "
    202             << "Most Visited.";
    203   if (!tmp_string.empty())
    204     mvp.thumbnail_url = GURL(tmp_string);
    205 
    206   r = args->GetString(4, &tmp_string);
    207   DCHECK(r) << "Missing index in addPinnedURL from the NTP Most Visited.";
    208   base::StringToInt(tmp_string, &index);
    209 
    210   AddPinnedURL(mvp, index);
    211 }
    212 
    213 void MostVisitedHandler::AddPinnedURL(const MostVisitedPage& page, int index) {
    214   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    215   if (ts)
    216     ts->AddPinnedURL(page.url, index);
    217 }
    218 
    219 void MostVisitedHandler::HandleRemovePinnedURL(const ListValue* args) {
    220   std::string url = UTF16ToUTF8(ExtractStringValue(args));
    221   RemovePinnedURL(GURL(url));
    222 }
    223 
    224 void MostVisitedHandler::RemovePinnedURL(const GURL& url) {
    225   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    226   if (ts)
    227     ts->RemovePinnedURL(url);
    228 }
    229 
    230 bool MostVisitedHandler::GetPinnedURLAtIndex(int index,
    231                                              MostVisitedPage* page) {
    232   // This iterates over all the pinned URLs. It might seem like it is worth
    233   // having a map from the index to the item but the number of items is limited
    234   // to the number of items the most visited section is showing on the NTP so
    235   // this will be fast enough for now.
    236   PrefService* prefs = web_ui_->GetProfile()->GetPrefs();
    237   const DictionaryValue* pinned_urls =
    238       prefs->GetDictionary(prefs::kNTPMostVisitedPinnedURLs);
    239   for (DictionaryValue::key_iterator it = pinned_urls->begin_keys();
    240       it != pinned_urls->end_keys(); ++it) {
    241     Value* value;
    242     if (pinned_urls->GetWithoutPathExpansion(*it, &value)) {
    243       if (!value->IsType(DictionaryValue::TYPE_DICTIONARY)) {
    244         // Moved on to TopSites and now going back.
    245         DictionaryPrefUpdate update(prefs, prefs::kNTPMostVisitedPinnedURLs);
    246         update.Get()->Clear();
    247         return false;
    248       }
    249 
    250       int dict_index;
    251       const DictionaryValue* dict = static_cast<DictionaryValue*>(value);
    252       if (dict->GetInteger("index", &dict_index) && dict_index == index) {
    253         // The favicon and thumbnail URLs may be empty.
    254         std::string tmp_string;
    255         if (dict->GetString("faviconUrl", &tmp_string))
    256           page->favicon_url = GURL(tmp_string);
    257         if (dict->GetString("thumbnailUrl", &tmp_string))
    258           page->thumbnail_url = GURL(tmp_string);
    259 
    260         if (dict->GetString("url", &tmp_string))
    261           page->url = GURL(tmp_string);
    262         else
    263           return false;
    264 
    265         return dict->GetString("title", &page->title);
    266       }
    267     } else {
    268       NOTREACHED() << "DictionaryValue iterators are filthy liars.";
    269     }
    270   }
    271 
    272   return false;
    273 }
    274 
    275 void MostVisitedHandler::SetPagesValueFromTopSites(
    276     const history::MostVisitedURLList& data) {
    277   pages_value_.reset(new ListValue);
    278   for (size_t i = 0; i < data.size(); i++) {
    279     const history::MostVisitedURL& url = data[i];
    280     DictionaryValue* page_value = new DictionaryValue();
    281     if (url.url.is_empty()) {
    282       page_value->SetBoolean("filler", true);
    283       pages_value_->Append(page_value);
    284       continue;
    285     }
    286 
    287     NewTabUI::SetURLTitleAndDirection(page_value,
    288                                       url.title,
    289                                       url.url);
    290     if (!url.favicon_url.is_empty())
    291       page_value->SetString("faviconUrl", url.favicon_url.spec());
    292 
    293     // Special case for prepopulated pages: thumbnailUrl is different from url.
    294     if (url.url.spec() == l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)) {
    295       page_value->SetString("thumbnailUrl",
    296           "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL");
    297     } else if (url.url.spec() ==
    298                l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)) {
    299       page_value->SetString("thumbnailUrl",
    300           "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL");
    301     }
    302 
    303     history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    304     if (ts && ts->IsURLPinned(url.url))
    305       page_value->SetBoolean("pinned", true);
    306     pages_value_->Append(page_value);
    307   }
    308 }
    309 
    310 void MostVisitedHandler::OnMostVisitedURLsAvailable(
    311     const history::MostVisitedURLList& data) {
    312   SetPagesValueFromTopSites(data);
    313   if (got_first_most_visited_request_) {
    314     SendPagesValue();
    315   }
    316 }
    317 
    318 bool MostVisitedHandler::IsFirstRun() {
    319   // If we found no pages we treat this as the first run.
    320   bool first_run = NewTabUI::NewTabHTMLSource::first_run() &&
    321       pages_value_->GetSize() ==
    322           MostVisitedHandler::GetPrePopulatedPages().size();
    323   // but first_run should only be true once.
    324   NewTabUI::NewTabHTMLSource::set_first_run(false);
    325   return first_run;
    326 }
    327 
    328 // static
    329 const std::vector<MostVisitedHandler::MostVisitedPage>&
    330     MostVisitedHandler::GetPrePopulatedPages() {
    331   // TODO(arv): This needs to get the data from some configurable place.
    332   // http://crbug.com/17630
    333   static std::vector<MostVisitedPage> pages;
    334   if (pages.empty()) {
    335     MostVisitedPage welcome_page = {
    336         l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE),
    337         GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)),
    338         GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL"),
    339         GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON")};
    340     pages.push_back(welcome_page);
    341 
    342     MostVisitedPage gallery_page = {
    343         l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE),
    344         GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)),
    345         GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL"),
    346         GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON")};
    347     pages.push_back(gallery_page);
    348   }
    349 
    350   return pages;
    351 }
    352 
    353 void MostVisitedHandler::Observe(NotificationType type,
    354                                  const NotificationSource& source,
    355                                  const NotificationDetails& details) {
    356   if (type != NotificationType::HISTORY_URLS_DELETED) {
    357     NOTREACHED();
    358     return;
    359   }
    360 
    361   // Some URLs were deleted from history.  Reload the most visited list.
    362   HandleGetMostVisited(NULL);
    363 }
    364 
    365 void MostVisitedHandler::BlacklistURL(const GURL& url) {
    366   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    367   if (ts)
    368     ts->AddBlacklistedURL(url);
    369 }
    370 
    371 std::string MostVisitedHandler::GetDictionaryKeyForURL(const std::string& url) {
    372   return MD5String(url);
    373 }
    374 
    375 // static
    376 void MostVisitedHandler::RegisterUserPrefs(PrefService* prefs) {
    377   prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedURLsBlacklist);
    378   prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedPinnedURLs);
    379 }
    380 
    381 // static
    382 std::vector<GURL> MostVisitedHandler::GetPrePopulatedUrls() {
    383   const std::vector<MostVisitedPage> pages =
    384       MostVisitedHandler::GetPrePopulatedPages();
    385   std::vector<GURL> page_urls;
    386   for (size_t i = 0; i < pages.size(); ++i)
    387     page_urls.push_back(pages[i].url);
    388   return page_urls;
    389 }
    390