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.
5 #include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.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/ui/browser.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
31 #include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
32 #include "chrome/browser/ui/webui/favicon_source.h"
33 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
34 #include "chrome/browser/ui/webui/ntp/ntp_stats.h"
35 #include "chrome/browser/ui/webui/ntp/thumbnail_list_source.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/user_prefs/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"
53 using base::UserMetricsAction
;
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) {
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
)) {
70 content::PageTransitionStripQualifier(entry
->GetTransitionType());
73 UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction", action_id
,
74 NUM_NTP_FOLLOW_ACTIONS
);
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));
84 // Set up our sources for top-sites data.
85 content::URLDataSource::Add(profile
, new ThumbnailListSource(profile
));
87 #if defined(OS_ANDROID)
88 // Register chrome://touch-icon as a data source for touch icons or favicons.
89 content::URLDataSource::Add(profile
,
90 new FaviconSource(profile
, FaviconSource::ANY
));
92 // Register chrome://favicon as a data source for favicons.
93 content::URLDataSource::Add(
94 profile
, new FaviconSource(profile
, FaviconSource::FAVICON
));
96 history::TopSites
* ts
= profile
->GetTopSites();
98 // TopSites updates itself after a delay. This is especially noticable when
99 // your profile is empty. Ask TopSites to update itself when we're about to
100 // show the new tab page.
101 ts
->SyncWithHistory();
103 // Register for notification when TopSites changes so that we can update
105 registrar_
.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED
,
106 content::Source
<history::TopSites
>(ts
));
109 // We pre-emptively make a fetch for the most visited pages so we have the
111 StartQueryForMostVisited();
113 web_ui()->RegisterMessageCallback("getMostVisited",
114 base::Bind(&MostVisitedHandler::HandleGetMostVisited
,
115 base::Unretained(this)));
117 // Register ourselves for any most-visited item blacklisting.
118 web_ui()->RegisterMessageCallback("blacklistURLFromMostVisited",
119 base::Bind(&MostVisitedHandler::HandleBlacklistUrl
,
120 base::Unretained(this)));
121 web_ui()->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
122 base::Bind(&MostVisitedHandler::HandleRemoveUrlsFromBlacklist
,
123 base::Unretained(this)));
124 web_ui()->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
125 base::Bind(&MostVisitedHandler::HandleClearBlacklist
,
126 base::Unretained(this)));
127 web_ui()->RegisterMessageCallback("mostVisitedAction",
128 base::Bind(&MostVisitedHandler::HandleMostVisitedAction
,
129 base::Unretained(this)));
130 web_ui()->RegisterMessageCallback("mostVisitedSelected",
131 base::Bind(&MostVisitedHandler::HandleMostVisitedSelected
,
132 base::Unretained(this)));
135 void MostVisitedHandler::HandleGetMostVisited(const base::ListValue
* args
) {
136 if (!got_first_most_visited_request_
) {
137 // If our initial data is already here, return it.
139 got_first_most_visited_request_
= true;
141 StartQueryForMostVisited();
145 void MostVisitedHandler::SendPagesValue() {
147 Profile
* profile
= Profile::FromWebUI(web_ui());
148 const base::DictionaryValue
* url_blacklist
=
149 profile
->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist
);
150 bool has_blacklisted_urls
= !url_blacklist
->empty();
151 history::TopSites
* ts
= profile
->GetTopSites();
153 has_blacklisted_urls
= ts
->HasBlacklistedItems();
155 MaybeRemovePageValues();
158 base::FundamentalValue
has_blacklisted_urls_value(has_blacklisted_urls
);
159 web_ui()->CallJavascriptFunction("ntp.setMostVisitedPages",
161 has_blacklisted_urls_value
);
162 pages_value_
.reset();
166 void MostVisitedHandler::StartQueryForMostVisited() {
167 history::TopSites
* ts
= Profile::FromWebUI(web_ui())->GetTopSites();
169 ts
->GetMostVisitedURLs(
170 base::Bind(&MostVisitedHandler::OnMostVisitedUrlsAvailable
,
171 weak_ptr_factory_
.GetWeakPtr()), false);
175 void MostVisitedHandler::HandleBlacklistUrl(const base::ListValue
* args
) {
176 std::string url
= base::UTF16ToUTF8(ExtractStringValue(args
));
177 BlacklistUrl(GURL(url
));
180 void MostVisitedHandler::HandleRemoveUrlsFromBlacklist(
181 const base::ListValue
* args
) {
182 DCHECK(args
->GetSize() != 0);
184 for (base::ListValue::const_iterator iter
= args
->begin();
185 iter
!= args
->end(); ++iter
) {
187 bool r
= (*iter
)->GetAsString(&url
);
192 content::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"));
193 history::TopSites
* ts
= Profile::FromWebUI(web_ui())->GetTopSites();
195 ts
->RemoveBlacklistedURL(GURL(url
));
199 void MostVisitedHandler::HandleClearBlacklist(const base::ListValue
* args
) {
200 content::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"));
202 history::TopSites
* ts
= Profile::FromWebUI(web_ui())->GetTopSites();
204 ts
->ClearBlacklistedURLs();
207 void MostVisitedHandler::HandleMostVisitedAction(const base::ListValue
* args
) {
211 if (!args
->GetDouble(0, &action_id
))
214 UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction",
215 static_cast<int>(action_id
),
216 NUM_NTP_FOLLOW_ACTIONS
);
217 most_visited_viewed_
= true;
218 user_action_logged_
= true;
221 void MostVisitedHandler::HandleMostVisitedSelected(
222 const base::ListValue
* args
) {
223 most_visited_viewed_
= true;
226 void MostVisitedHandler::SetPagesValueFromTopSites(
227 const history::MostVisitedURLList
& data
) {
228 pages_value_
.reset(new base::ListValue
);
230 history::MostVisitedURLList
top_sites(data
);
231 history::MostVisitedTilesExperiment::MaybeShuffle(&top_sites
);
233 for (size_t i
= 0; i
< top_sites
.size(); i
++) {
234 const history::MostVisitedURL
& url
= top_sites
[i
];
235 base::DictionaryValue
* page_value
= new base::DictionaryValue();
236 if (url
.url
.is_empty()) {
237 page_value
->SetBoolean("filler", true);
238 pages_value_
->Append(page_value
);
242 NewTabUI::SetUrlTitleAndDirection(page_value
,
245 pages_value_
->Append(page_value
);
249 void MostVisitedHandler::OnMostVisitedUrlsAvailable(
250 const history::MostVisitedURLList
& data
) {
251 SetPagesValueFromTopSites(data
);
252 if (got_first_most_visited_request_
) {
257 void MostVisitedHandler::Observe(int type
,
258 const content::NotificationSource
& source
,
259 const content::NotificationDetails
& details
) {
260 DCHECK_EQ(type
, chrome::NOTIFICATION_TOP_SITES_CHANGED
);
262 // Most visited urls changed, query again.
263 StartQueryForMostVisited();
266 void MostVisitedHandler::BlacklistUrl(const GURL
& url
) {
267 history::TopSites
* ts
= Profile::FromWebUI(web_ui())->GetTopSites();
269 ts
->AddBlacklistedURL(url
);
270 content::RecordAction(UserMetricsAction("MostVisited_UrlBlacklisted"));
273 std::string
MostVisitedHandler::GetDictionaryKeyForUrl(const std::string
& url
) {
274 return base::MD5String(url
);
277 void MostVisitedHandler::MaybeRemovePageValues() {
278 // The code below uses APIs not available on Android and the experiment should
280 #if !defined(OS_ANDROID)
281 if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
284 TabStripModel
* tab_strip_model
= chrome::FindBrowserWithWebContents(
285 web_ui()->GetWebContents())->tab_strip_model();
286 history::TopSites
* top_sites
= Profile::FromWebUI(web_ui())->GetTopSites();
287 if (!tab_strip_model
|| !top_sites
) {
292 std::set
<std::string
> open_urls
;
293 chrome::GetOpenUrls(*tab_strip_model
, *top_sites
, &open_urls
);
294 history::MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
301 void MostVisitedHandler::RegisterProfilePrefs(
302 user_prefs::PrefRegistrySyncable
* registry
) {
303 registry
->RegisterDictionaryPref(
304 prefs::kNtpMostVisitedURLsBlacklist
,
305 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);