普通文本  |  390行  |  13.55 KB

// Copyright (c) 2011 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/most_visited_handler.h"

#include <set>

#include "base/callback.h"
#include "base/command_line.h"
#include "base/md5.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/singleton.h"
#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/history/page_usage_data.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/new_tab_ui.h"
#include "chrome/browser/ui/webui/thumbnail_source.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
#include "googleurl/src/gurl.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

// The number of most visited pages we show.
const size_t kMostVisitedPages = 8;

// The number of days of history we consider for most visited entries.
const int kMostVisitedScope = 90;

}  // namespace

// This struct is used when getting the pre-populated pages in case the user
// hasn't filled up his most visited pages.
struct MostVisitedHandler::MostVisitedPage {
  string16 title;
  GURL url;
  GURL thumbnail_url;
  GURL favicon_url;
};

MostVisitedHandler::MostVisitedHandler()
    : got_first_most_visited_request_(false) {
}

MostVisitedHandler::~MostVisitedHandler() {
}

WebUIMessageHandler* MostVisitedHandler::Attach(WebUI* web_ui) {
  Profile* profile = web_ui->GetProfile();
  // Set up our sources for thumbnail and favicon data.
  ThumbnailSource* thumbnail_src = new ThumbnailSource(profile);
  profile->GetChromeURLDataManager()->AddDataSource(thumbnail_src);

  profile->GetChromeURLDataManager()->AddDataSource(new FaviconSource(profile));

  // Get notifications when history is cleared.
  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
                 Source<Profile>(profile));

  WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);

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

void MostVisitedHandler::RegisterMessages() {
  // Register ourselves as the handler for the "mostvisited" message from
  // Javascript.
  web_ui_->RegisterMessageCallback("getMostVisited",
      NewCallback(this, &MostVisitedHandler::HandleGetMostVisited));

  // Register ourselves for any most-visited item blacklisting.
  web_ui_->RegisterMessageCallback("blacklistURLFromMostVisited",
      NewCallback(this, &MostVisitedHandler::HandleBlacklistURL));
  web_ui_->RegisterMessageCallback("removeURLsFromMostVisitedBlacklist",
      NewCallback(this, &MostVisitedHandler::HandleRemoveURLsFromBlacklist));
  web_ui_->RegisterMessageCallback("clearMostVisitedURLsBlacklist",
      NewCallback(this, &MostVisitedHandler::HandleClearBlacklist));

  // Register ourself for pinned URL messages.
  web_ui_->RegisterMessageCallback("addPinnedURL",
      NewCallback(this, &MostVisitedHandler::HandleAddPinnedURL));
  web_ui_->RegisterMessageCallback("removePinnedURL",
      NewCallback(this, &MostVisitedHandler::HandleRemovePinnedURL));
}

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

void MostVisitedHandler::SendPagesValue() {
  if (pages_value_.get()) {
    Profile* profile = web_ui_->GetProfile();
    const 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();
    FundamentalValue first_run(IsFirstRun());
    FundamentalValue has_blacklisted_urls_value(has_blacklisted_urls);
    web_ui_->CallJavascriptFunction("mostVisitedPages",
                                    *(pages_value_.get()),
                                    first_run,
                                    has_blacklisted_urls_value);
    pages_value_.reset();
  }
}

void MostVisitedHandler::StartQueryForMostVisited() {
  // Use TopSites.
  history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
  if (ts) {
    ts->GetMostVisitedURLs(
        &topsites_consumer_,
        NewCallback(this, &MostVisitedHandler::OnMostVisitedURLsAvailable));
  }
}

void MostVisitedHandler::HandleBlacklistURL(const ListValue* args) {
  std::string url = UTF16ToUTF8(ExtractStringValue(args));
  BlacklistURL(GURL(url));
}

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

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

void MostVisitedHandler::HandleClearBlacklist(const ListValue* args) {
  UserMetrics::RecordAction(UserMetricsAction("MostVisited_BlacklistCleared"),
                            web_ui_->GetProfile());

  history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
  if (ts)
    ts->ClearBlacklistedURLs();
}

void MostVisitedHandler::HandleAddPinnedURL(const ListValue* args) {
  DCHECK_EQ(5U, args->GetSize()) << "Wrong number of params to addPinnedURL";
  MostVisitedPage mvp;
  std::string tmp_string;
  string16 tmp_string16;
  int index;

  bool r = args->GetString(0, &tmp_string);
  DCHECK(r) << "Missing URL in addPinnedURL from the NTP Most Visited.";
  mvp.url = GURL(tmp_string);

  r = args->GetString(1, &tmp_string16);
  DCHECK(r) << "Missing title in addPinnedURL from the NTP Most Visited.";
  mvp.title = tmp_string16;

  r = args->GetString(2, &tmp_string);
  DCHECK(r) << "Failed to read the favicon URL in addPinnedURL from the NTP "
            << "Most Visited.";
  if (!tmp_string.empty())
    mvp.favicon_url = GURL(tmp_string);

  r = args->GetString(3, &tmp_string);
  DCHECK(r) << "Failed to read the thumbnail URL in addPinnedURL from the NTP "
            << "Most Visited.";
  if (!tmp_string.empty())
    mvp.thumbnail_url = GURL(tmp_string);

  r = args->GetString(4, &tmp_string);
  DCHECK(r) << "Missing index in addPinnedURL from the NTP Most Visited.";
  base::StringToInt(tmp_string, &index);

  AddPinnedURL(mvp, index);
}

void MostVisitedHandler::AddPinnedURL(const MostVisitedPage& page, int index) {
  history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
  if (ts)
    ts->AddPinnedURL(page.url, index);
}

void MostVisitedHandler::HandleRemovePinnedURL(const ListValue* args) {
  std::string url = UTF16ToUTF8(ExtractStringValue(args));
  RemovePinnedURL(GURL(url));
}

void MostVisitedHandler::RemovePinnedURL(const GURL& url) {
  history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
  if (ts)
    ts->RemovePinnedURL(url);
}

bool MostVisitedHandler::GetPinnedURLAtIndex(int index,
                                             MostVisitedPage* page) {
  // This iterates over all the pinned URLs. It might seem like it is worth
  // having a map from the index to the item but the number of items is limited
  // to the number of items the most visited section is showing on the NTP so
  // this will be fast enough for now.
  PrefService* prefs = web_ui_->GetProfile()->GetPrefs();
  const DictionaryValue* pinned_urls =
      prefs->GetDictionary(prefs::kNTPMostVisitedPinnedURLs);
  for (DictionaryValue::key_iterator it = pinned_urls->begin_keys();
      it != pinned_urls->end_keys(); ++it) {
    Value* value;
    if (pinned_urls->GetWithoutPathExpansion(*it, &value)) {
      if (!value->IsType(DictionaryValue::TYPE_DICTIONARY)) {
        // Moved on to TopSites and now going back.
        DictionaryPrefUpdate update(prefs, prefs::kNTPMostVisitedPinnedURLs);
        update.Get()->Clear();
        return false;
      }

      int dict_index;
      const DictionaryValue* dict = static_cast<DictionaryValue*>(value);
      if (dict->GetInteger("index", &dict_index) && dict_index == index) {
        // The favicon and thumbnail URLs may be empty.
        std::string tmp_string;
        if (dict->GetString("faviconUrl", &tmp_string))
          page->favicon_url = GURL(tmp_string);
        if (dict->GetString("thumbnailUrl", &tmp_string))
          page->thumbnail_url = GURL(tmp_string);

        if (dict->GetString("url", &tmp_string))
          page->url = GURL(tmp_string);
        else
          return false;

        return dict->GetString("title", &page->title);
      }
    } else {
      NOTREACHED() << "DictionaryValue iterators are filthy liars.";
    }
  }

  return false;
}

void MostVisitedHandler::SetPagesValueFromTopSites(
    const history::MostVisitedURLList& data) {
  pages_value_.reset(new ListValue);
  for (size_t i = 0; i < data.size(); i++) {
    const history::MostVisitedURL& url = data[i];
    DictionaryValue* page_value = new 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);
    if (!url.favicon_url.is_empty())
      page_value->SetString("faviconUrl", url.favicon_url.spec());

    // Special case for prepopulated pages: thumbnailUrl is different from url.
    if (url.url.spec() == l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)) {
      page_value->SetString("thumbnailUrl",
          "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL");
    } else if (url.url.spec() ==
               l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)) {
      page_value->SetString("thumbnailUrl",
          "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL");
    }

    history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
    if (ts && ts->IsURLPinned(url.url))
      page_value->SetBoolean("pinned", true);
    pages_value_->Append(page_value);
  }
}

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

bool MostVisitedHandler::IsFirstRun() {
  // If we found no pages we treat this as the first run.
  bool first_run = NewTabUI::NewTabHTMLSource::first_run() &&
      pages_value_->GetSize() ==
          MostVisitedHandler::GetPrePopulatedPages().size();
  // but first_run should only be true once.
  NewTabUI::NewTabHTMLSource::set_first_run(false);
  return first_run;
}

// static
const std::vector<MostVisitedHandler::MostVisitedPage>&
    MostVisitedHandler::GetPrePopulatedPages() {
  // TODO(arv): This needs to get the data from some configurable place.
  // http://crbug.com/17630
  static std::vector<MostVisitedPage> pages;
  if (pages.empty()) {
    MostVisitedPage welcome_page = {
        l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE),
        GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL)),
        GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL"),
        GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON")};
    pages.push_back(welcome_page);

    MostVisitedPage gallery_page = {
        l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE),
        GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL)),
        GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_THUMBNAIL"),
        GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON")};
    pages.push_back(gallery_page);
  }

  return pages;
}

void MostVisitedHandler::Observe(NotificationType type,
                                 const NotificationSource& source,
                                 const NotificationDetails& details) {
  if (type != NotificationType::HISTORY_URLS_DELETED) {
    NOTREACHED();
    return;
  }

  // Some URLs were deleted from history.  Reload the most visited list.
  HandleGetMostVisited(NULL);
}

void MostVisitedHandler::BlacklistURL(const GURL& url) {
  history::TopSites* ts = web_ui_->GetProfile()->GetTopSites();
  if (ts)
    ts->AddBlacklistedURL(url);
}

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

// static
void MostVisitedHandler::RegisterUserPrefs(PrefService* prefs) {
  prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedURLsBlacklist);
  prefs->RegisterDictionaryPref(prefs::kNTPMostVisitedPinnedURLs);
}

// static
std::vector<GURL> MostVisitedHandler::GetPrePopulatedUrls() {
  const std::vector<MostVisitedPage> pages =
      MostVisitedHandler::GetPrePopulatedPages();
  std::vector<GURL> page_urls;
  for (size_t i = 0; i < pages.size(); ++i)
    page_urls.push_back(pages[i].url);
  return page_urls;
}