• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/most_visited_handler.h"
6 
7 #include <set>
8 
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/md5.h"
12 #include "base/memory/scoped_vector.h"
13 #include "base/memory/singleton.h"
14 #include "base/string16.h"
15 #include "base/string_number_conversions.h"
16 #include "base/threading/thread.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/browser/history/page_usage_data.h"
20 #include "chrome/browser/history/top_sites.h"
21 #include "chrome/browser/metrics/user_metrics.h"
22 #include "chrome/browser/prefs/pref_service.h"
23 #include "chrome/browser/prefs/scoped_user_pref_update.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
26 #include "chrome/browser/ui/webui/favicon_source.h"
27 #include "chrome/browser/ui/webui/new_tab_ui.h"
28 #include "chrome/browser/ui/webui/thumbnail_source.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/common/url_constants.h"
31 #include "content/browser/browser_thread.h"
32 #include "content/common/notification_source.h"
33 #include "content/common/notification_type.h"
34 #include "googleurl/src/gurl.h"
35 #include "grit/chromium_strings.h"
36 #include "grit/generated_resources.h"
37 #include "grit/locale_settings.h"
38 #include "ui/base/l10n/l10n_util.h"
39 
40 namespace {
41 
42 // The number of most visited pages we show.
43 const size_t kMostVisitedPages = 8;
44 
45 // The number of days of history we consider for most visited entries.
46 const int kMostVisitedScope = 90;
47 
48 }  // namespace
49 
50 // This struct is used when getting the pre-populated pages in case the user
51 // hasn't filled up his most visited pages.
52 struct MostVisitedHandler::MostVisitedPage {
53   string16 title;
54   GURL url;
55   GURL thumbnail_url;
56   GURL favicon_url;
57 };
58 
MostVisitedHandler()59 MostVisitedHandler::MostVisitedHandler()
60     : got_first_most_visited_request_(false) {
61 }
62 
~MostVisitedHandler()63 MostVisitedHandler::~MostVisitedHandler() {
64 }
65 
Attach(WebUI * web_ui)66 WebUIMessageHandler* MostVisitedHandler::Attach(WebUI* web_ui) {
67   Profile* profile = web_ui->GetProfile();
68   // Set up our sources for thumbnail and favicon data.
69   ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
70   profile->GetChromeURLDataManager()->AddDataSource(thumbnail_src);
71 
72   profile->GetChromeURLDataManager()->AddDataSource(new FaviconSource(profile));
73 
74   // Get notifications when history is cleared.
75   registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
76                  Source<Profile>(profile));
77 
78   WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
79 
80   // We pre-emptively make a fetch for the most visited pages so we have the
81   // results sooner.
82   StartQueryForMostVisited();
83   return result;
84 }
85 
RegisterMessages()86 void MostVisitedHandler::RegisterMessages() {
87   // Register ourselves as the handler for the "mostvisited" message from
88   // Javascript.
89   web_ui_->RegisterMessageCallback("getMostVisited",
90       NewCallback(this, &MostVisitedHandler::HandleGetMostVisited));
91 
92   // Register ourselves for any most-visited item blacklisting.
93   web_ui_->RegisterMessageCallback("blacklistURLFromMostVisited",
94       NewCallback(this, &MostVisitedHandler::HandleBlacklistURL));
95   web_ui_->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
96       NewCallback(this, &MostVisitedHandler::HandleRemoveURLsFromBlacklist));
97   web_ui_->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
98       NewCallback(this, &MostVisitedHandler::HandleClearBlacklist));
99 
100   // Register ourself for pinned URL messages.
101   web_ui_->RegisterMessageCallback("addPinnedURL",
102       NewCallback(this, &MostVisitedHandler::HandleAddPinnedURL));
103   web_ui_->RegisterMessageCallback("removePinnedURL",
104       NewCallback(this, &MostVisitedHandler::HandleRemovePinnedURL));
105 }
106 
HandleGetMostVisited(const ListValue * args)107 void MostVisitedHandler::HandleGetMostVisited(const ListValue* args) {
108   if (!got_first_most_visited_request_) {
109     // If our intial data is already here, return it.
110     SendPagesValue();
111     got_first_most_visited_request_ = true;
112   } else {
113     StartQueryForMostVisited();
114   }
115 }
116 
SendPagesValue()117 void MostVisitedHandler::SendPagesValue() {
118   if (pages_value_.get()) {
119     Profile* profile = web_ui_->GetProfile();
120     const DictionaryValue* url_blacklist =
121         profile->GetPrefs()->GetDictionary(prefs::kNTPMostVisitedURLsBlacklist);
122     bool has_blacklisted_urls = !url_blacklist->empty();
123     history::TopSites* ts = profile->GetTopSites();
124     if (ts)
125       has_blacklisted_urls = ts->HasBlacklistedItems();
126     FundamentalValue first_run(IsFirstRun());
127     FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
128     web_ui_->CallJavascriptFunction("mostVisitedPages",
129                                     *(pages_value_.get()),
130                                     first_run,
131                                     has_blacklisted_urls_value);
132     pages_value_.reset();
133   }
134 }
135 
StartQueryForMostVisited()136 void MostVisitedHandler::StartQueryForMostVisited() {
137   // Use TopSites.
138   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
139   if (ts) {
140     ts->GetMostVisitedURLs(
141         &topsites_consumer_,
142         NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable));
143   }
144 }
145 
HandleBlacklistURL(const ListValue * args)146 void MostVisitedHandler::HandleBlacklistURL(const ListValue* args) {
147   std::string url = UTF16ToUTF8(ExtractStringValue(args));
148   BlacklistURL(GURL(url));
149 }
150 
HandleRemoveURLsFromBlacklist(const ListValue * args)151 void MostVisitedHandler::HandleRemoveURLsFromBlacklist(const ListValue* args) {
152   DCHECK(args->GetSize() != 0);
153 
154   for (ListValue::const_iterator iter = args->begin();
155        iter != args->end(); ++iter) {
156     std::string url;
157     bool r = (*iter)->GetAsString(&url);
158     if (!r) {
159       NOTREACHED();
160       return;
161     }
162     UserMetrics::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"),
163                               web_ui_->GetProfile());
164     history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
165     if (ts)
166       ts->RemoveBlacklistedURL(GURL(url));
167   }
168 }
169 
HandleClearBlacklist(const ListValue * args)170 void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) {
171   UserMetrics::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"),
172                             web_ui_->GetProfile());
173 
174   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
175   if (ts)
176     ts->ClearBlacklistedURLs();
177 }
178 
HandleAddPinnedURL(const ListValue * args)179 void MostVisitedHandler::HandleAddPinnedURL(const ListValue* args) {
180   DCHECK_EQ(5U, args->GetSize()) << "Wrong number of params to addPinnedURL";
181   MostVisitedPage mvp;
182   std::string tmp_string;
183   string16 tmp_string16;
184   int index;
185 
186   bool r = args->GetString(0, &tmp_string);
187   DCHECK(r) << "Missing URL in addPinnedURL from the NTP Most Visited.";
188   mvp.url = GURL(tmp_string);
189 
190   r = args->GetString(1, &tmp_string16);
191   DCHECK(r) << "Missing title in addPinnedURL from the NTP Most Visited.";
192   mvp.title = tmp_string16;
193 
194   r = args->GetString(2, &tmp_string);
195   DCHECK(r) << "Failed to read the favicon URL in addPinnedURL from the NTP "
196             << "Most Visited.";
197   if (!tmp_string.empty())
198     mvp.favicon_url = GURL(tmp_string);
199 
200   r = args->GetString(3, &tmp_string);
201   DCHECK(r) << "Failed to read the thumbnail URL in addPinnedURL from the NTP "
202             << "Most Visited.";
203   if (!tmp_string.empty())
204     mvp.thumbnail_url = GURL(tmp_string);
205 
206   r = args->GetString(4, &tmp_string);
207   DCHECK(r) << "Missing index in addPinnedURL from the NTP Most Visited.";
208   base::StringToInt(tmp_string, &index);
209 
210   AddPinnedURL(mvp, index);
211 }
212 
AddPinnedURL(const MostVisitedPage & page,int index)213 void MostVisitedHandler::AddPinnedURL(const MostVisitedPage& page, int index) {
214   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
215   if (ts)
216     ts->AddPinnedURL(page.url, index);
217 }
218 
HandleRemovePinnedURL(const ListValue * args)219 void MostVisitedHandler::HandleRemovePinnedURL(const ListValue* args) {
220   std::string url = UTF16ToUTF8(ExtractStringValue(args));
221   RemovePinnedURL(GURL(url));
222 }
223 
RemovePinnedURL(const GURL & url)224 void MostVisitedHandler::RemovePinnedURL(const GURL& url) {
225   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
226   if (ts)
227     ts->RemovePinnedURL(url);
228 }
229 
GetPinnedURLAtIndex(int index,MostVisitedPage * page)230 bool MostVisitedHandler::GetPinnedURLAtIndex(int index,
231                                              MostVisitedPage* page) {
232   // This iterates over all the pinned URLs. It might seem like it is worth
233   // having a map from the index to the item but the number of items is limited
234   // to the number of items the most visited section is showing on the NTP so
235   // this will be fast enough for now.
236   PrefService* prefs = web_ui_->GetProfile()->GetPrefs();
237   const DictionaryValue* pinned_urls =
238       prefs->GetDictionary(prefs::kNTPMostVisitedPinnedURLs);
239   for (DictionaryValue::key_iterator it = pinned_urls->begin_keys();
240       it != pinned_urls->end_keys(); ++it) {
241     Value* value;
242     if (pinned_urls->GetWithoutPathExpansion(*it, &value)) {
243       if (!value->IsType(DictionaryValue::TYPE_DICTIONARY)) {
244         // Moved on to TopSites and now going back.
245         DictionaryPrefUpdate update(prefs, prefs::kNTPMostVisitedPinnedURLs);
246         update.Get()->Clear();
247         return false;
248       }
249 
250       int dict_index;
251       const DictionaryValue* dict = static_cast<DictionaryValue*>(value);
252       if (dict->GetInteger("index", &dict_index) && dict_index == index) {
253         // The favicon and thumbnail URLs may be empty.
254         std::string tmp_string;
255         if (dict->GetString("faviconUrl", &tmp_string))
256           page->favicon_url = GURL(tmp_string);
257         if (dict->GetString("thumbnailUrl", &tmp_string))
258           page->thumbnail_url = GURL(tmp_string);
259 
260         if (dict->GetString("url", &tmp_string))
261           page->url = GURL(tmp_string);
262         else
263           return false;
264 
265         return dict->GetString("title", &page->title);
266       }
267     } else {
268       NOTREACHED() << "DictionaryValue iterators are filthy liars.";
269     }
270   }
271 
272   return false;
273 }
274 
SetPagesValueFromTopSites(const history::MostVisitedURLList & data)275 void MostVisitedHandler::SetPagesValueFromTopSites(
276     const history::MostVisitedURLList& data) {
277   pages_value_.reset(new ListValue);
278   for (size_t i = 0; i < data.size(); i++) {
279     const history::MostVisitedURL& url = data[i];
280     DictionaryValue* page_value = new DictionaryValue();
281     if (url.url.is_empty()) {
282       page_value->SetBoolean("filler", true);
283       pages_value_->Append(page_value);
284       continue;
285     }
286 
287     NewTabUI::SetURLTitleAndDirection(page_value,
288                                       url.title,
289                                       url.url);
290     if (!url.favicon_url.is_empty())
291       page_value->SetString("faviconUrl", url.favicon_url.spec());
292 
293     // Special case for prepopulated pages: thumbnailUrl is different from url.
294     if (url.url.spec() == l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)) {
295       page_value->SetString("thumbnailUrl",
296           "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL");
297     } else if (url.url.spec() ==
298                l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)) {
299       page_value->SetString("thumbnailUrl",
300           "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL");
301     }
302 
303     history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
304     if (ts && ts->IsURLPinned(url.url))
305       page_value->SetBoolean("pinned", true);
306     pages_value_->Append(page_value);
307   }
308 }
309 
OnMostVisitedURLsAvailable(const history::MostVisitedURLList & data)310 void MostVisitedHandler::OnMostVisitedURLsAvailable(
311     const history::MostVisitedURLList& data) {
312   SetPagesValueFromTopSites(data);
313   if (got_first_most_visited_request_) {
314     SendPagesValue();
315   }
316 }
317 
IsFirstRun()318 bool MostVisitedHandler::IsFirstRun() {
319   // If we found no pages we treat this as the first run.
320   bool first_run = NewTabUI::NewTabHTMLSource::first_run() &&
321       pages_value_->GetSize() ==
322           MostVisitedHandler::GetPrePopulatedPages().size();
323   // but first_run should only be true once.
324   NewTabUI::NewTabHTMLSource::set_first_run(false);
325   return first_run;
326 }
327 
328 // static
329 const std::vector<MostVisitedHandler::MostVisitedPage>&
GetPrePopulatedPages()330     MostVisitedHandler::GetPrePopulatedPages() {
331   // TODO(arv): This needs to get the data from some configurable place.
332   // http://crbug.com/17630
333   static std::vector<MostVisitedPage> pages;
334   if (pages.empty()) {
335     MostVisitedPage welcome_page = {
336         l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE),
337         GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)),
338         GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL"),
339         GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON")};
340     pages.push_back(welcome_page);
341 
342     MostVisitedPage gallery_page = {
343         l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE),
344         GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)),
345         GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL"),
346         GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON")};
347     pages.push_back(gallery_page);
348   }
349 
350   return pages;
351 }
352 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)353 void MostVisitedHandler::Observe(NotificationType type,
354                                  const NotificationSource& source,
355                                  const NotificationDetails& details) {
356   if (type != NotificationType::HISTORY_URLS_DELETED) {
357     NOTREACHED();
358     return;
359   }
360 
361   // Some URLs were deleted from history.  Reload the most visited list.
362   HandleGetMostVisited(NULL);
363 }
364 
BlacklistURL(const GURL & url)365 void MostVisitedHandler::BlacklistURL(const GURL& url) {
366   history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
367   if (ts)
368     ts->AddBlacklistedURL(url);
369 }
370 
GetDictionaryKeyForURL(const std::string & url)371 std::string MostVisitedHandler::GetDictionaryKeyForURL(const std::string& url) {
372   return MD5String(url);
373 }
374 
375 // static
RegisterUserPrefs(PrefService * prefs)376 void MostVisitedHandler::RegisterUserPrefs(PrefService* prefs) {
377   prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedURLsBlacklist);
378   prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedPinnedURLs);
379 }
380 
381 // static
GetPrePopulatedUrls()382 std::vector<GURL> MostVisitedHandler::GetPrePopulatedUrls() {
383   const std::vector<MostVisitedPage> pages =
384       MostVisitedHandler::GetPrePopulatedPages();
385   std::vector<GURL> page_urls;
386   for (size_t i = 0; i < pages.size(); ++i)
387     page_urls.push_back(pages[i].url);
388   return page_urls;
389 }
390