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