most_visited_handler.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.
4
5#include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
6
7#include <set>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/command_line.h"
12#include "base/md5.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/string16.h"
18#include "base/strings/string_number_conversions.h"
19#include "base/threading/thread.h"
20#include "base/utf_string_conversions.h"
21#include "base/values.h"
22#include "chrome/browser/history/page_usage_data.h"
23#include "chrome/browser/history/top_sites.h"
24#include "chrome/browser/prefs/scoped_user_pref_update.h"
25#include "chrome/browser/profiles/profile.h"
26#include "chrome/browser/ui/webui/favicon_source.h"
27#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
28#include "chrome/browser/ui/webui/ntp/ntp_stats.h"
29#include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
30#include "chrome/common/chrome_notification_types.h"
31#include "chrome/common/pref_names.h"
32#include "chrome/common/url_constants.h"
33#include "components/user_prefs/pref_registry_syncable.h"
34#include "content/public/browser/navigation_controller.h"
35#include "content/public/browser/navigation_entry.h"
36#include "content/public/browser/notification_source.h"
37#include "content/public/browser/url_data_source.h"
38#include "content/public/browser/user_metrics.h"
39#include "content/public/browser/web_contents.h"
40#include "content/public/browser/web_ui.h"
41#include "googleurl/src/gurl.h"
42#include "grit/chromium_strings.h"
43#include "grit/generated_resources.h"
44#include "grit/locale_settings.h"
45#include "ui/base/l10n/l10n_util.h"
46
47using content::UserMetricsAction;
48
49MostVisitedHandler::MostVisitedHandler()
50    : weak_ptr_factory_(this),
51      got_first_most_visited_request_(false),
52      most_visited_viewed_(false),
53      user_action_logged_(false) {
54}
55
56MostVisitedHandler::~MostVisitedHandler() {
57  if (!user_action_logged_ && most_visited_viewed_) {
58    const GURL ntp_url = GURL(chrome::kChromeUINewTabURL);
59    int action_id = NTP_FOLLOW_ACTION_OTHER;
60    content::NavigationEntry* entry =
61        web_ui()->GetWebContents()->GetController().GetActiveEntry();
62    if (entry && (entry->GetURL() != ntp_url)) {
63      action_id =
64          content::PageTransitionStripQualifier(entry->GetTransitionType());
65    }
66
67    UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction", action_id,
68                              NUM_NTP_FOLLOW_ACTIONS);
69  }
70}
71
72void MostVisitedHandler::RegisterMessages() {
73  Profile* profile = Profile::FromWebUI(web_ui());
74  // Set up our sources for thumbnail and favicon data.
75  ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
76  content::URLDataSource::Add(profile, thumbnail_src);
77
78#if defined(OS_ANDROID)
79  // Register chrome://touch-icon as a data source for touch icons or favicons.
80  content::URLDataSource::Add(profile,
81                              new FaviconSource(profile, FaviconSource::ANY));
82#endif
83  // Register chrome://favicon as a data source for favicons.
84  content::URLDataSource::Add(
85      profile, new FaviconSource(profile, FaviconSource::FAVICON));
86
87  history::TopSites* ts = profile->GetTopSites();
88  if (ts) {
89    // TopSites updates itself after a delay. This is especially noticable when
90    // your profile is empty. Ask TopSites to update itself when we're about to
91    // show the new tab page.
92    ts->SyncWithHistory();
93
94    // Register for notification when TopSites changes so that we can update
95    // ourself.
96    registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
97                   content::Source<history::TopSites>(ts));
98  }
99
100  // We pre-emptively make a fetch for the most visited pages so we have the
101  // results sooner.
102  StartQueryForMostVisited();
103
104  web_ui()->RegisterMessageCallback("getMostVisited",
105      base::Bind(&MostVisitedHandler::HandleGetMostVisited,
106                 base::Unretained(this)));
107
108  // Register ourselves for any most-visited item blacklisting.
109  web_ui()->RegisterMessageCallback("blacklistURLFromMostVisited",
110      base::Bind(&MostVisitedHandler::HandleBlacklistUrl,
111                 base::Unretained(this)));
112  web_ui()->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
113      base::Bind(&MostVisitedHandler::HandleRemoveUrlsFromBlacklist,
114                 base::Unretained(this)));
115  web_ui()->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
116      base::Bind(&MostVisitedHandler::HandleClearBlacklist,
117                 base::Unretained(this)));
118  web_ui()->RegisterMessageCallback("mostVisitedAction",
119      base::Bind(&MostVisitedHandler::HandleMostVisitedAction,
120                 base::Unretained(this)));
121  web_ui()->RegisterMessageCallback("mostVisitedSelected",
122      base::Bind(&MostVisitedHandler::HandleMostVisitedSelected,
123                 base::Unretained(this)));
124}
125
126void MostVisitedHandler::HandleGetMostVisited(const ListValue* args) {
127  if (!got_first_most_visited_request_) {
128    // If our initial data is already here, return it.
129    SendPagesValue();
130    got_first_most_visited_request_ = true;
131  } else {
132    StartQueryForMostVisited();
133  }
134}
135
136void MostVisitedHandler::SendPagesValue() {
137  if (pages_value_.get()) {
138    Profile* profile = Profile::FromWebUI(web_ui());
139    const DictionaryValue* url_blacklist =
140        profile->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist);
141    bool has_blacklisted_urls = !url_blacklist->empty();
142    history::TopSites* ts = profile->GetTopSites();
143    if (ts)
144      has_blacklisted_urls = ts->HasBlacklistedItems();
145    base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
146    web_ui()->CallJavascriptFunction("ntp.setMostVisitedPages",
147                                     *(pages_value_.get()),
148                                     has_blacklisted_urls_value);
149    pages_value_.reset();
150  }
151}
152
153void MostVisitedHandler::StartQueryForMostVisited() {
154  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
155  if (ts) {
156    ts->GetMostVisitedURLs(
157        base::Bind(&MostVisitedHandler::OnMostVisitedUrlsAvailable,
158                   weak_ptr_factory_.GetWeakPtr()));
159  }
160}
161
162void MostVisitedHandler::HandleBlacklistUrl(const ListValue* args) {
163  std::string url = UTF16ToUTF8(ExtractStringValue(args));
164  BlacklistUrl(GURL(url));
165}
166
167void MostVisitedHandler::HandleRemoveUrlsFromBlacklist(const ListValue* args) {
168  DCHECK(args->GetSize() != 0);
169
170  for (ListValue::const_iterator iter = args->begin();
171       iter != args->end(); ++iter) {
172    std::string url;
173    bool r = (*iter)->GetAsString(&url);
174    if (!r) {
175      NOTREACHED();
176      return;
177    }
178    content::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"));
179    history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
180    if (ts)
181      ts->RemoveBlacklistedURL(GURL(url));
182  }
183}
184
185void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) {
186  content::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"));
187
188  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
189  if (ts)
190    ts->ClearBlacklistedURLs();
191}
192
193void MostVisitedHandler::HandleMostVisitedAction(const base::ListValue* args) {
194  DCHECK(args);
195
196  double action_id;
197  if (!args->GetDouble(0, &action_id))
198    NOTREACHED();
199
200  UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction",
201                            static_cast<int>(action_id),
202                            NUM_NTP_FOLLOW_ACTIONS);
203  most_visited_viewed_ = true;
204  user_action_logged_ = true;
205}
206
207void MostVisitedHandler::HandleMostVisitedSelected(
208    const base::ListValue* args) {
209  most_visited_viewed_ = true;
210}
211
212void MostVisitedHandler::SetPagesValueFromTopSites(
213    const history::MostVisitedURLList& data) {
214  pages_value_.reset(new ListValue);
215
216  history::MostVisitedURLList top_sites(data);
217  history::TopSites::MaybeShuffle(&top_sites);
218
219  for (size_t i = 0; i < top_sites.size(); i++) {
220    const history::MostVisitedURL& url = top_sites[i];
221    DictionaryValue* page_value = new DictionaryValue();
222    if (url.url.is_empty()) {
223      page_value->SetBoolean("filler", true);
224      pages_value_->Append(page_value);
225      continue;
226    }
227
228    NewTabUI::SetUrlTitleAndDirection(page_value,
229                                      url.title,
230                                      url.url);
231    pages_value_->Append(page_value);
232  }
233}
234
235void MostVisitedHandler::OnMostVisitedUrlsAvailable(
236    const history::MostVisitedURLList& data) {
237  SetPagesValueFromTopSites(data);
238  if (got_first_most_visited_request_) {
239    SendPagesValue();
240  }
241}
242
243void MostVisitedHandler::Observe(int type,
244                                 const content::NotificationSource& source,
245                                 const content::NotificationDetails& details) {
246  DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
247
248  // Most visited urls changed, query again.
249  StartQueryForMostVisited();
250}
251
252void MostVisitedHandler::BlacklistUrl(const GURL& url) {
253  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
254  if (ts)
255    ts->AddBlacklistedURL(url);
256  content::RecordAction(UserMetricsAction("MostVisited_UrlBlacklisted"));
257}
258
259std::string MostVisitedHandler::GetDictionaryKeyForUrl(const std::string& url) {
260  return base::MD5String(url);
261}
262
263// static
264void MostVisitedHandler::RegisterUserPrefs(
265    user_prefs::PrefRegistrySyncable* registry) {
266  registry->RegisterDictionaryPref(
267      prefs::kNtpMostVisitedURLsBlacklist,
268      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
269}
270