root/chrome/browser/ui/webui/ntp/most_visited_handler.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_ptr_factory_
  2. RegisterMessages
  3. HandleGetMostVisited
  4. SendPagesValue
  5. StartQueryForMostVisited
  6. HandleBlacklistUrl
  7. HandleRemoveUrlsFromBlacklist
  8. HandleClearBlacklist
  9. HandleMostVisitedAction
  10. HandleMostVisitedSelected
  11. SetPagesValueFromTopSites
  12. OnMostVisitedUrlsAvailable
  13. Observe
  14. BlacklistUrl
  15. GetDictionaryKeyForUrl
  16. MaybeRemovePageValues
  17. RegisterProfilePrefs

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ntp/most_visited_handler.h"

#include <set>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/md5.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/history/most_visited_tiles_experiment.h"
#include "chrome/browser/history/page_usage_data.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_utils.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/browser/ui/webui/ntp/ntp_stats.h"
#include "chrome/browser/ui/webui/ntp/thumbnail_list_source.h"
#include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"

using base::UserMetricsAction;

MostVisitedHandler::MostVisitedHandler()
    : got_first_most_visited_request_(false),
      most_visited_viewed_(false),
      user_action_logged_(false),
      weak_ptr_factory_(this) {
}

MostVisitedHandler::~MostVisitedHandler() {
  if (!user_action_logged_ && most_visited_viewed_) {
    const GURL ntp_url = GURL(chrome::kChromeUINewTabURL);
    int action_id = NTP_FOLLOW_ACTION_OTHER;
    content::NavigationEntry* entry =
        web_ui()->GetWebContents()->GetController().GetLastCommittedEntry();
    if (entry && (entry->GetURL() != ntp_url)) {
      action_id =
          content::PageTransitionStripQualifier(entry->GetTransitionType());
    }

    UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction", action_id,
                              NUM_NTP_FOLLOW_ACTIONS);
  }
}

void MostVisitedHandler::RegisterMessages() {
  Profile* profile = Profile::FromWebUI(web_ui());
  // Set up our sources for thumbnail and favicon data.
  content::URLDataSource::Add(profile, new ThumbnailSource(profile, false));
  content::URLDataSource::Add(profile, new ThumbnailSource(profile, true));

  // Set up our sources for top-sites data.
  content::URLDataSource::Add(profile, new ThumbnailListSource(profile));

#if defined(OS_ANDROID)
  // Register chrome://touch-icon as a data source for touch icons or favicons.
  content::URLDataSource::Add(profile,
                              new FaviconSource(profile, FaviconSource::ANY));
#endif
  // Register chrome://favicon as a data source for favicons.
  content::URLDataSource::Add(
      profile, new FaviconSource(profile, FaviconSource::FAVICON));

  history::TopSites* ts = profile->GetTopSites();
  if (ts) {
    // TopSites updates itself after a delay. This is especially noticable when
    // your profile is empty. Ask TopSites to update itself when we're about to
    // show the new tab page.
    ts->SyncWithHistory();

    // Register for notification when TopSites changes so that we can update
    // ourself.
    registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
                   content::Source<history::TopSites>(ts));
  }

  // We pre-emptively make a fetch for the most visited pages so we have the
  // results sooner.
  StartQueryForMostVisited();

  web_ui()->RegisterMessageCallback("getMostVisited",
      base::Bind(&MostVisitedHandler::HandleGetMostVisited,
                 base::Unretained(this)));

  // Register ourselves for any most-visited item blacklisting.
  web_ui()->RegisterMessageCallback("blacklistURLFromMostVisited",
      base::Bind(&MostVisitedHandler::HandleBlacklistUrl,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
      base::Bind(&MostVisitedHandler::HandleRemoveUrlsFromBlacklist,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
      base::Bind(&MostVisitedHandler::HandleClearBlacklist,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("mostVisitedAction",
      base::Bind(&MostVisitedHandler::HandleMostVisitedAction,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("mostVisitedSelected",
      base::Bind(&MostVisitedHandler::HandleMostVisitedSelected,
                 base::Unretained(this)));
}

void MostVisitedHandler::HandleGetMostVisited(const base::ListValue* args) {
  if (!got_first_most_visited_request_) {
    // If our initial data is already here, return it.
    SendPagesValue();
    got_first_most_visited_request_ = true;
  } else {
    StartQueryForMostVisited();
  }
}

void MostVisitedHandler::SendPagesValue() {
  if (pages_value_) {
    Profile* profile = Profile::FromWebUI(web_ui());
    const base::DictionaryValue* url_blacklist =
        profile->GetPrefs()->GetDictionary(prefs::kNtpMostVisitedURLsBlacklist);
    bool has_blacklisted_urls = !url_blacklist->empty();
    history::TopSites* ts = profile->GetTopSites();
    if (ts) {
      has_blacklisted_urls = ts->HasBlacklistedItems();

      MaybeRemovePageValues();
    }

    base::FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
    web_ui()->CallJavascriptFunction("ntp.setMostVisitedPages",
                                     *pages_value_,
                                     has_blacklisted_urls_value);
    pages_value_.reset();
  }
}

void MostVisitedHandler::StartQueryForMostVisited() {
  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
  if (ts) {
    ts->GetMostVisitedURLs(
        base::Bind(&MostVisitedHandler::OnMostVisitedUrlsAvailable,
                   weak_ptr_factory_.GetWeakPtr()), false);
  }
}

void MostVisitedHandler::HandleBlacklistUrl(const base::ListValue* args) {
  std::string url = base::UTF16ToUTF8(ExtractStringValue(args));
  BlacklistUrl(GURL(url));
}

void MostVisitedHandler::HandleRemoveUrlsFromBlacklist(
    const base::ListValue* args) {
  DCHECK(args->GetSize() != 0);

  for (base::ListValue::const_iterator iter = args->begin();
       iter != args->end(); ++iter) {
    std::string url;
    bool r = (*iter)->GetAsString(&url);
    if (!r) {
      NOTREACHED();
      return;
    }
    content::RecordAction(UserMetricsAction("MostVisited_UrlRemoved"));
    history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
    if (ts)
      ts->RemoveBlacklistedURL(GURL(url));
  }
}

void MostVisitedHandler::HandleClearBlacklist(const base::ListValue* args) {
  content::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"));

  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
  if (ts)
    ts->ClearBlacklistedURLs();
}

void MostVisitedHandler::HandleMostVisitedAction(const base::ListValue* args) {
  DCHECK(args);

  double action_id;
  if (!args->GetDouble(0, &action_id))
    NOTREACHED();

  UMA_HISTOGRAM_ENUMERATION("NewTabPage.MostVisitedAction",
                            static_cast<int>(action_id),
                            NUM_NTP_FOLLOW_ACTIONS);
  most_visited_viewed_ = true;
  user_action_logged_ = true;
}

void MostVisitedHandler::HandleMostVisitedSelected(
    const base::ListValue* args) {
  most_visited_viewed_ = true;
}

void MostVisitedHandler::SetPagesValueFromTopSites(
    const history::MostVisitedURLList& data) {
  pages_value_.reset(new base::ListValue);

  history::MostVisitedURLList top_sites(data);
  history::MostVisitedTilesExperiment::MaybeShuffle(&top_sites);

  for (size_t i = 0; i < top_sites.size(); i++) {
    const history::MostVisitedURL& url = top_sites[i];
    base::DictionaryValue* page_value = new base::DictionaryValue();
    if (url.url.is_empty()) {
      page_value->SetBoolean("filler", true);
      pages_value_->Append(page_value);
      continue;
    }

    NewTabUI::SetUrlTitleAndDirection(page_value,
                                      url.title,
                                      url.url);
    pages_value_->Append(page_value);
  }
}

void MostVisitedHandler::OnMostVisitedUrlsAvailable(
    const history::MostVisitedURLList& data) {
  SetPagesValueFromTopSites(data);
  if (got_first_most_visited_request_) {
    SendPagesValue();
  }
}

void MostVisitedHandler::Observe(int type,
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
  DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);

  // Most visited urls changed, query again.
  StartQueryForMostVisited();
}

void MostVisitedHandler::BlacklistUrl(const GURL& url) {
  history::TopSites* ts = Profile::FromWebUI(web_ui())->GetTopSites();
  if (ts)
    ts->AddBlacklistedURL(url);
  content::RecordAction(UserMetricsAction("MostVisited_UrlBlacklisted"));
}

std::string MostVisitedHandler::GetDictionaryKeyForUrl(const std::string& url) {
  return base::MD5String(url);
}

void MostVisitedHandler::MaybeRemovePageValues() {
// The code below uses APIs not available on Android and the experiment should
// not run there.
#if !defined(OS_ANDROID)
  if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled())
    return;

  TabStripModel* tab_strip_model = chrome::FindBrowserWithWebContents(
      web_ui()->GetWebContents())->tab_strip_model();
  history::TopSites* top_sites = Profile::FromWebUI(web_ui())->GetTopSites();
  if (!tab_strip_model || !top_sites) {
    NOTREACHED();
    return;
  }

  std::set<std::string> open_urls;
  chrome::GetOpenUrls(*tab_strip_model, *top_sites, &open_urls);
  history::MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
      open_urls,
      pages_value_.get());
#endif
}

// static
void MostVisitedHandler::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterDictionaryPref(
      prefs::kNtpMostVisitedURLsBlacklist,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}

/* [<][>][^][v][top][bottom][index][help] */