suggestions_page_handler.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/suggestions_page_handler.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <math.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/md5.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h" 14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string16.h" 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h" 187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/page_usage_data.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/top_sites.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/favicon_source.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/ntp_stats.h" 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/webui/ntp/suggestions_combiner.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/suggestions_source_top_sites.h" 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/webui/ntp/thumbnail_source.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h" 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/pref_registry/pref_registry_syncable.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_controller.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_entry.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h" 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/url_data_source.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/user_metrics.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_ui.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/page_transition_types.h" 377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h" 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::UserMetricsAction; 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SuggestionsHandler::SuggestionsHandler() 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : got_first_suggestions_request_(false), 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_viewed_(false), 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_action_logged_(false) { 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SuggestionsHandler::~SuggestionsHandler() { 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!user_action_logged_ && suggestions_viewed_) { 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const GURL ntp_url = GURL(chrome::kChromeUINewTabURL); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int action_id = NTP_FOLLOW_ACTION_OTHER; 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content::NavigationEntry* entry = 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) web_ui()->GetWebContents()->GetController().GetLastCommittedEntry(); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (entry && (entry->GetURL() != ntp_url)) { 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) action_id = 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content::PageTransitionStripQualifier(entry->GetTransitionType()); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestedSitesAction", action_id, 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NUM_NTP_FOLLOW_ACTIONS); 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::RegisterMessages() { 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Profile* profile = Profile::FromWebUI(web_ui()); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set up our sources for thumbnail and favicon data. 66d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) content::URLDataSource::Add(profile, new ThumbnailSource(profile, false)); 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::URLDataSource::Add( 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) profile, new FaviconSource(profile, FaviconSource::FAVICON)); 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) change the source of the web-sites to provide our data. 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Initial commit uses top sites as a data source. 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) history::TopSites* top_sites = profile->GetTopSites(); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (top_sites) { 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TopSites updates itself after a delay. This is especially noticable when 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // your profile is empty. Ask TopSites to update itself when we're about to 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // show the new tab page. 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) top_sites->SyncWithHistory(); 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Register for notification when TopSites changes so that we can update 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ourself. 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content::Source<history::TopSites>(top_sites)); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Setup the suggestions sources. 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SuggestionsCombiner* combiner = new SuggestionsCombiner(this, profile); 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) combiner->AddSource(new SuggestionsSourceTopSites()); 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_combiner_.reset(combiner); 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We pre-emptively make a fetch for suggestions so we have the results 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // sooner. 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_combiner_->FetchItems(profile); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("getSuggestions", 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleGetSuggestions, 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Register ourselves for any suggestions item blacklisting. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("blacklistURLFromSuggestions", 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleBlacklistURL, 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("removeURLsFromSuggestionsBlacklist", 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleRemoveURLsFromBlacklist, 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("clearSuggestionsURLsBlacklist", 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleClearBlacklist, 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("suggestedSitesAction", 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleSuggestedSitesAction, 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->RegisterMessageCallback("suggestedSitesSelected", 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&SuggestionsHandler::HandleSuggestedSitesSelected, 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Unretained(this))); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SuggestionsHandler::HandleGetSuggestions(const base::ListValue* args) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!got_first_suggestions_request_) { 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If it's the first request we get, return the prefetched data. 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SendPagesValue(); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) got_first_suggestions_request_ = true; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_combiner_->FetchItems(Profile::FromWebUI(web_ui())); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::OnSuggestionsReady() { 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we got the results as a result of a suggestions request initiated by the 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // JavaScript then we send back the page values. 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (got_first_suggestions_request_) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SendPagesValue(); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::SendPagesValue() { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (suggestions_combiner_->GetPageValues()) { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) add actual blacklist. 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_blacklisted_urls = false; 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) web_ui()->CallJavascriptFunction("ntp.setSuggestionsPages", 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *suggestions_combiner_->GetPageValues(), 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) has_blacklisted_urls_value); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SuggestionsHandler::HandleBlacklistURL(const base::ListValue* args) { 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string url = base::UTF16ToUTF8(ExtractStringValue(args)); 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BlacklistURL(GURL(url)); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SuggestionsHandler::HandleRemoveURLsFromBlacklist( 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::ListValue* args) { 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(args->GetSize(), 0U); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) remove URLs from blacklist. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SuggestionsHandler::HandleClearBlacklist(const base::ListValue* args) { 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) clear blacklist. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::HandleSuggestedSitesAction( 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::ListValue* args) { 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(args); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double action_id; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!args->GetDouble(0, &action_id)) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestedSitesAction", 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int>(action_id), 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NUM_NTP_FOLLOW_ACTIONS); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_viewed_ = true; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_action_logged_ = true; 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::HandleSuggestedSitesSelected( 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::ListValue* args) { 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_viewed_ = true; 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::Observe(int type, 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const content::NotificationSource& source, 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const content::NotificationDetails& details) { 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Suggestions urls changed, query again. 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suggestions_combiner_->FetchItems(Profile::FromWebUI(web_ui())); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SuggestionsHandler::BlacklistURL(const GURL& url) { 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) blacklist an URL. 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string SuggestionsHandler::GetDictionaryKeyForURL(const std::string& url) { 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base::MD5String(url); 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid SuggestionsHandler::RegisterProfilePrefs( 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) user_prefs::PrefRegistrySyncable* registry) { 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(georgey) add user preferences (such as own blacklist) as needed. 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 200