diff options
Diffstat (limited to 'chrome/browser/ui/webui/ntp/most_visited_handler.cc')
-rw-r--r-- | chrome/browser/ui/webui/ntp/most_visited_handler.cc | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/chrome/browser/ui/webui/ntp/most_visited_handler.cc b/chrome/browser/ui/webui/ntp/most_visited_handler.cc new file mode 100644 index 0000000..8cc405d --- /dev/null +++ b/chrome/browser/ui/webui/ntp/most_visited_handler.cc @@ -0,0 +1,380 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/ntp/most_visited_handler.h" + +#include <set> + +#include "base/callback.h" +#include "base/command_line.h" +#include "base/md5.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/singleton.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/threading/thread.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/history/page_usage_data.h" +#include "chrome/browser/history/top_sites.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/chrome_url_data_manager.h" +#include "chrome/browser/ui/webui/favicon_source.h" +#include "chrome/browser/ui/webui/ntp/new_tab_ui.h" +#include "chrome/browser/ui/webui/ntp/thumbnail_source.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "content/browser/browser_thread.h" +#include "content/common/notification_source.h" +#include "content/common/notification_type.h" +#include "googleurl/src/gurl.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "ui/base/l10n/l10n_util.h" + +// This struct is used when getting the pre-populated pages in case the user +// hasn't filled up his most visited pages. +struct MostVisitedHandler::MostVisitedPage { + string16 title; + GURL url; + GURL thumbnail_url; + GURL favicon_url; +}; + +MostVisitedHandler::MostVisitedHandler() + : got_first_most_visited_request_(false) { +} + +MostVisitedHandler::~MostVisitedHandler() { +} + +WebUIMessageHandler* MostVisitedHandler::Attach(WebUI* web_ui) { + Profile* profile = web_ui->GetProfile(); + // Set up our sources for thumbnail and favicon data. + ThumbnailSource* thumbnail_src = new ThumbnailSource(profile); + profile->GetChromeURLDataManager()->AddDataSource(thumbnail_src); + + profile->GetChromeURLDataManager()->AddDataSource( + new FaviconSource(profile, FaviconSource::FAVICON)); + + // Get notifications when history is cleared. + registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, + Source<Profile>(profile)); + + WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui); + + // We pre-emptively make a fetch for the most visited pages so we have the + // results sooner. + StartQueryForMostVisited(); + return result; +} + +void MostVisitedHandler::RegisterMessages() { + // Register ourselves as the handler for the "mostvisited" message from + // Javascript. + web_ui_->RegisterMessageCallback("getMostVisited", + NewCallback(this, &MostVisitedHandler::HandleGetMostVisited)); + + // Register ourselves for any most-visited item blacklisting. + web_ui_->RegisterMessageCallback("blacklistURLFromMostVisited", + NewCallback(this, &MostVisitedHandler::HandleBlacklistURL)); + web_ui_->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist", + NewCallback(this, &MostVisitedHandler::HandleRemoveURLsFromBlacklist)); + web_ui_->RegisterMessageCallback("clearMostVisitedURLsBlacklist", + NewCallback(this, &MostVisitedHandler::HandleClearBlacklist)); + + // Register ourself for pinned URL messages. + web_ui_->RegisterMessageCallback("addPinnedURL", + NewCallback(this, &MostVisitedHandler::HandleAddPinnedURL)); + web_ui_->RegisterMessageCallback("removePinnedURL", + NewCallback(this, &MostVisitedHandler::HandleRemovePinnedURL)); +} + +void MostVisitedHandler::HandleGetMostVisited(const ListValue* args) { + if (!got_first_most_visited_request_) { + // If our intial data is already here, return it. + SendPagesValue(); + got_first_most_visited_request_ = true; + } else { + StartQueryForMostVisited(); + } +} + +void MostVisitedHandler::SendPagesValue() { + if (pages_value_.get()) { + Profile* profile = web_ui_->GetProfile(); + const DictionaryValue* url_blacklist = + profile->GetPrefs()->GetDictionary(prefs::kNTPMostVisitedURLsBlacklist); + bool has_blacklisted_urls = !url_blacklist->empty(); + history::TopSites* ts = profile->GetTopSites(); + if (ts) + has_blacklisted_urls = ts->HasBlacklistedItems(); + FundamentalValue first_run(IsFirstRun()); + FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls); + web_ui_->CallJavascriptFunction("mostVisitedPages", + *(pages_value_.get()), + first_run, + has_blacklisted_urls_value); + pages_value_.reset(); + } +} + +void MostVisitedHandler::StartQueryForMostVisited() { + // Use TopSites. + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) { + ts->GetMostVisitedURLs( + &topsites_consumer_, + NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable)); + } +} + +void MostVisitedHandler::HandleBlacklistURL(const ListValue* args) { + std::string url = UTF16ToUTF8(ExtractStringValue(args)); + BlacklistURL(GURL(url)); +} + +void MostVisitedHandler::HandleRemoveURLsFromBlacklist(const ListValue* args) { + DCHECK(args->GetSize() != 0); + + for (ListValue::const_iterator iter = args->begin(); + iter != args->end(); ++iter) { + std::string url; + bool r = (*iter)->GetAsString(&url); + if (!r) { + NOTREACHED(); + return; + } + UserMetrics::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"), + web_ui_->GetProfile()); + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) + ts->RemoveBlacklistedURL(GURL(url)); + } +} + +void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) { + UserMetrics::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"), + web_ui_->GetProfile()); + + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) + ts->ClearBlacklistedURLs(); +} + +void MostVisitedHandler::HandleAddPinnedURL(const ListValue* args) { + DCHECK_EQ(5U, args->GetSize()) << "Wrong number of params to addPinnedURL"; + MostVisitedPage mvp; + std::string tmp_string; + string16 tmp_string16; + int index; + + bool r = args->GetString(0, &tmp_string); + DCHECK(r) << "Missing URL in addPinnedURL from the NTP Most Visited."; + mvp.url = GURL(tmp_string); + + r = args->GetString(1, &tmp_string16); + DCHECK(r) << "Missing title in addPinnedURL from the NTP Most Visited."; + mvp.title = tmp_string16; + + r = args->GetString(2, &tmp_string); + DCHECK(r) << "Failed to read the favicon URL in addPinnedURL from the NTP " + << "Most Visited."; + if (!tmp_string.empty()) + mvp.favicon_url = GURL(tmp_string); + + r = args->GetString(3, &tmp_string); + DCHECK(r) << "Failed to read the thumbnail URL in addPinnedURL from the NTP " + << "Most Visited."; + if (!tmp_string.empty()) + mvp.thumbnail_url = GURL(tmp_string); + + r = args->GetString(4, &tmp_string); + DCHECK(r) << "Missing index in addPinnedURL from the NTP Most Visited."; + base::StringToInt(tmp_string, &index); + + AddPinnedURL(mvp, index); +} + +void MostVisitedHandler::AddPinnedURL(const MostVisitedPage& page, int index) { + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) + ts->AddPinnedURL(page.url, index); +} + +void MostVisitedHandler::HandleRemovePinnedURL(const ListValue* args) { + std::string url = UTF16ToUTF8(ExtractStringValue(args)); + RemovePinnedURL(GURL(url)); +} + +void MostVisitedHandler::RemovePinnedURL(const GURL& url) { + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) + ts->RemovePinnedURL(url); +} + +bool MostVisitedHandler::GetPinnedURLAtIndex(int index, + MostVisitedPage* page) { + // This iterates over all the pinned URLs. It might seem like it is worth + // having a map from the index to the item but the number of items is limited + // to the number of items the most visited section is showing on the NTP so + // this will be fast enough for now. + PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); + const DictionaryValue* pinned_urls = + prefs->GetDictionary(prefs::kNTPMostVisitedPinnedURLs); + for (DictionaryValue::key_iterator it = pinned_urls->begin_keys(); + it != pinned_urls->end_keys(); ++it) { + Value* value; + if (pinned_urls->GetWithoutPathExpansion(*it, &value)) { + if (!value->IsType(DictionaryValue::TYPE_DICTIONARY)) { + // Moved on to TopSites and now going back. + DictionaryPrefUpdate update(prefs, prefs::kNTPMostVisitedPinnedURLs); + update.Get()->Clear(); + return false; + } + + int dict_index; + const DictionaryValue* dict = static_cast<DictionaryValue*>(value); + if (dict->GetInteger("index", &dict_index) && dict_index == index) { + // The favicon and thumbnail URLs may be empty. + std::string tmp_string; + if (dict->GetString("faviconUrl", &tmp_string)) + page->favicon_url = GURL(tmp_string); + if (dict->GetString("thumbnailUrl", &tmp_string)) + page->thumbnail_url = GURL(tmp_string); + + if (dict->GetString("url", &tmp_string)) + page->url = GURL(tmp_string); + else + return false; + + return dict->GetString("title", &page->title); + } + } else { + NOTREACHED() << "DictionaryValue iterators are filthy liars."; + } + } + + return false; +} + +void MostVisitedHandler::SetPagesValueFromTopSites( + const history::MostVisitedURLList& data) { + pages_value_.reset(new ListValue); + for (size_t i = 0; i < data.size(); i++) { + const history::MostVisitedURL& url = data[i]; + DictionaryValue* page_value = new DictionaryValue(); + if (url.url.is_empty()) { + page_value->SetBoolean("filler", true); + pages_value_->Append(page_value); + continue; + } + + NewTabUI::SetURLTitleAndDirection(page_value, + url.title, + url.url); + if (!url.favicon_url.is_empty()) + page_value->SetString("faviconUrl", url.favicon_url.spec()); + + // Special case for prepopulated pages: thumbnailUrl is different from url. + if (url.url.spec() == l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)) { + page_value->SetString("thumbnailUrl", + "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL"); + } else if (url.url.spec() == + l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)) { + page_value->SetString("thumbnailUrl", + "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL"); + } + + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts && ts->IsURLPinned(url.url)) + page_value->SetBoolean("pinned", true); + pages_value_->Append(page_value); + } +} + +void MostVisitedHandler::OnMostVisitedURLsAvailable( + const history::MostVisitedURLList& data) { + SetPagesValueFromTopSites(data); + if (got_first_most_visited_request_) { + SendPagesValue(); + } +} + +bool MostVisitedHandler::IsFirstRun() { + // If we found no pages we treat this as the first run. + bool first_run = NewTabUI::NewTabHTMLSource::first_run() && + pages_value_->GetSize() == + MostVisitedHandler::GetPrePopulatedPages().size(); + // but first_run should only be true once. + NewTabUI::NewTabHTMLSource::set_first_run(false); + return first_run; +} + +// static +const std::vector<MostVisitedHandler::MostVisitedPage>& + MostVisitedHandler::GetPrePopulatedPages() { + // TODO(arv): This needs to get the data from some configurable place. + // http://crbug.com/17630 + static std::vector<MostVisitedPage> pages; + if (pages.empty()) { + MostVisitedPage welcome_page = { + l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE), + GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)), + GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL"), + GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON")}; + pages.push_back(welcome_page); + + MostVisitedPage gallery_page = { + l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE), + GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)), + GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL"), + GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON")}; + pages.push_back(gallery_page); + } + + return pages; +} + +void MostVisitedHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type != NotificationType::HISTORY_URLS_DELETED) { + NOTREACHED(); + return; + } + + // Some URLs were deleted from history. Reload the most visited list. + HandleGetMostVisited(NULL); +} + +void MostVisitedHandler::BlacklistURL(const GURL& url) { + history::TopSites* ts = web_ui_->GetProfile()->GetTopSites(); + if (ts) + ts->AddBlacklistedURL(url); +} + +std::string MostVisitedHandler::GetDictionaryKeyForURL(const std::string& url) { + return MD5String(url); +} + +// static +void MostVisitedHandler::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedURLsBlacklist); + prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedPinnedURLs); +} + +// static +std::vector<GURL> MostVisitedHandler::GetPrePopulatedUrls() { + const std::vector<MostVisitedPage> pages = + MostVisitedHandler::GetPrePopulatedPages(); + std::vector<GURL> page_urls; + for (size_t i = 0; i < pages.size(); ++i) + page_urls.push_back(pages[i].url); + return page_urls; +} |