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