suggestions_page_handler.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/suggestions_page_handler.h"
6
7#include <math.h>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/md5.h"
13#include "base/metrics/histogram.h"
14#include "base/strings/string16.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/threading/thread.h"
17#include "base/values.h"
18#include "chrome/browser/chrome_notification_types.h"
19#include "chrome/browser/history/top_sites.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/ui/webui/favicon_source.h"
22#include "chrome/browser/ui/webui/ntp/ntp_stats.h"
23#include "chrome/browser/ui/webui/ntp/suggestions_combiner.h"
24#include "chrome/browser/ui/webui/ntp/suggestions_source_top_sites.h"
25#include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
26#include "chrome/common/url_constants.h"
27#include "components/history/core/browser/page_usage_data.h"
28#include "components/pref_registry/pref_registry_syncable.h"
29#include "content/public/browser/navigation_controller.h"
30#include "content/public/browser/navigation_entry.h"
31#include "content/public/browser/notification_source.h"
32#include "content/public/browser/url_data_source.h"
33#include "content/public/browser/user_metrics.h"
34#include "content/public/browser/web_contents.h"
35#include "content/public/browser/web_ui.h"
36#include "ui/base/page_transition_types.h"
37#include "url/gurl.h"
38
39using base::UserMetricsAction;
40
41SuggestionsHandler::SuggestionsHandler()
42    : got_first_suggestions_request_(false),
43      suggestions_viewed_(false),
44      user_action_logged_(false) {
45}
46
47SuggestionsHandler::~SuggestionsHandler() {
48  if (!user_action_logged_ && suggestions_viewed_) {
49    const GURL ntp_url = GURL(chrome::kChromeUINewTabURL);
50    int action_id = NTP_FOLLOW_ACTION_OTHER;
51    content::NavigationEntry* entry =
52        web_ui()->GetWebContents()->GetController().GetLastCommittedEntry();
53    if (entry && (entry->GetURL() != ntp_url)) {
54      action_id =
55          ui::PageTransitionStripQualifier(entry->GetTransitionType());
56    }
57
58    UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestedSitesAction", action_id,
59                              NUM_NTP_FOLLOW_ACTIONS);
60  }
61}
62
63void SuggestionsHandler::RegisterMessages() {
64  Profile* profile = Profile::FromWebUI(web_ui());
65  // Set up our sources for thumbnail and favicon data.
66  content::URLDataSource::Add(profile, new ThumbnailSource(profile, false));
67  content::URLDataSource::Add(
68      profile, new FaviconSource(profile, FaviconSource::FAVICON));
69
70  // TODO(georgey) change the source of the web-sites to provide our data.
71  // Initial commit uses top sites as a data source.
72  history::TopSites* top_sites = profile->GetTopSites();
73  if (top_sites) {
74    // TopSites updates itself after a delay. This is especially noticable when
75    // your profile is empty. Ask TopSites to update itself when we're about to
76    // show the new tab page.
77    top_sites->SyncWithHistory();
78
79    // Register for notification when TopSites changes so that we can update
80    // ourself.
81    registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
82                   content::Source<history::TopSites>(top_sites));
83  }
84
85  // Setup the suggestions sources.
86  SuggestionsCombiner* combiner = new SuggestionsCombiner(this, profile);
87  combiner->AddSource(new SuggestionsSourceTopSites());
88  suggestions_combiner_.reset(combiner);
89
90  // We pre-emptively make a fetch for suggestions so we have the results
91  // sooner.
92  suggestions_combiner_->FetchItems(profile);
93
94  web_ui()->RegisterMessageCallback("getSuggestions",
95      base::Bind(&SuggestionsHandler::HandleGetSuggestions,
96                 base::Unretained(this)));
97  // Register ourselves for any suggestions item blacklisting.
98  web_ui()->RegisterMessageCallback("blacklistURLFromSuggestions",
99      base::Bind(&SuggestionsHandler::HandleBlacklistURL,
100                 base::Unretained(this)));
101  web_ui()->RegisterMessageCallback("removeURLsFromSuggestionsBlacklist",
102      base::Bind(&SuggestionsHandler::HandleRemoveURLsFromBlacklist,
103                 base::Unretained(this)));
104  web_ui()->RegisterMessageCallback("clearSuggestionsURLsBlacklist",
105      base::Bind(&SuggestionsHandler::HandleClearBlacklist,
106                 base::Unretained(this)));
107  web_ui()->RegisterMessageCallback("suggestedSitesAction",
108      base::Bind(&SuggestionsHandler::HandleSuggestedSitesAction,
109                 base::Unretained(this)));
110  web_ui()->RegisterMessageCallback("suggestedSitesSelected",
111      base::Bind(&SuggestionsHandler::HandleSuggestedSitesSelected,
112                 base::Unretained(this)));
113}
114
115void SuggestionsHandler::HandleGetSuggestions(const base::ListValue* args) {
116  if (!got_first_suggestions_request_) {
117    // If it's the first request we get, return the prefetched data.
118    SendPagesValue();
119    got_first_suggestions_request_ = true;
120  } else {
121    suggestions_combiner_->FetchItems(Profile::FromWebUI(web_ui()));
122  }
123}
124
125void SuggestionsHandler::OnSuggestionsReady() {
126  // If we got the results as a result of a suggestions request initiated by the
127  // JavaScript then we send back the page values.
128  if (got_first_suggestions_request_)
129    SendPagesValue();
130}
131
132void SuggestionsHandler::SendPagesValue() {
133  if (suggestions_combiner_->GetPageValues()) {
134    // TODO(georgey) add actual blacklist.
135    bool has_blacklisted_urls = false;
136    base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
137    web_ui()->CallJavascriptFunction("ntp.setSuggestionsPages",
138                                     *suggestions_combiner_->GetPageValues(),
139                                     has_blacklisted_urls_value);
140  }
141}
142
143void SuggestionsHandler::HandleBlacklistURL(const base::ListValue* args) {
144  std::string url = base::UTF16ToUTF8(ExtractStringValue(args));
145  BlacklistURL(GURL(url));
146}
147
148void SuggestionsHandler::HandleRemoveURLsFromBlacklist(
149    const base::ListValue* args) {
150  DCHECK_GT(args->GetSize(), 0U);
151  // TODO(georgey) remove URLs from blacklist.
152}
153
154void SuggestionsHandler::HandleClearBlacklist(const base::ListValue* args) {
155  // TODO(georgey) clear blacklist.
156}
157
158void SuggestionsHandler::HandleSuggestedSitesAction(
159    const base::ListValue* args) {
160  DCHECK(args);
161
162  double action_id;
163  if (!args->GetDouble(0, &action_id))
164    NOTREACHED();
165
166  UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestedSitesAction",
167                            static_cast<int>(action_id),
168                            NUM_NTP_FOLLOW_ACTIONS);
169  suggestions_viewed_ = true;
170  user_action_logged_ = true;
171}
172
173void SuggestionsHandler::HandleSuggestedSitesSelected(
174    const base::ListValue* args) {
175  suggestions_viewed_ = true;
176}
177
178void SuggestionsHandler::Observe(int type,
179                                 const content::NotificationSource& source,
180                                 const content::NotificationDetails& details) {
181  DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
182
183  // Suggestions urls changed, query again.
184  suggestions_combiner_->FetchItems(Profile::FromWebUI(web_ui()));
185}
186
187void SuggestionsHandler::BlacklistURL(const GURL& url) {
188  // TODO(georgey) blacklist an URL.
189}
190
191std::string SuggestionsHandler::GetDictionaryKeyForURL(const std::string& url) {
192  return base::MD5String(url);
193}
194
195// static
196void SuggestionsHandler::RegisterProfilePrefs(
197    user_prefs::PrefRegistrySyncable* registry) {
198  // TODO(georgey) add user preferences (such as own blacklist) as needed.
199}
200