root/chrome/browser/android/most_visited_sites.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ExtractMostVisitedTitlesAndURLs
  2. OnMostVisitedURLsAvailable
  3. ExtractThumbnail
  4. OnObtainedThumbnail
  5. GetUrlThumbnailTask
  6. weak_ptr_factory_
  7. Destroy
  8. SetMostVisitedURLsObserver
  9. GetURLThumbnail
  10. BlacklistUrl
  11. Observe
  12. Register
  13. QueryMostVisitedURLs
  14. InitiateTopSitesQuery
  15. OnSuggestionsProfileAvailable
  16. Init

// Copyright 2013 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/android/most_visited_sites.h"

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/search/suggestions/proto/suggestions.pb.h"
#include "chrome/browser/search/suggestions/suggestions_service.h"
#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_source.h"
#include "jni/MostVisitedSites_jni.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/codec/jpeg_codec.h"

using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::ConvertJavaStringToUTF8;
using base::android::ScopedJavaGlobalRef;
using base::android::ToJavaArrayOfStrings;
using base::android::CheckException;
using content::BrowserThread;
using history::TopSites;
using suggestions::ChromeSuggestion;
using suggestions::SuggestionsProfile;
using suggestions::SuggestionsService;
using suggestions::SuggestionsServiceFactory;

namespace {

void ExtractMostVisitedTitlesAndURLs(
    const history::MostVisitedURLList& visited_list,
    std::vector<base::string16>* titles,
    std::vector<std::string>* urls,
    int num_sites) {
  size_t max = static_cast<size_t>(num_sites);
  for (size_t i = 0; i < visited_list.size() && i < max; ++i) {
    const history::MostVisitedURL& visited = visited_list[i];

    if (visited.url.is_empty())
      break;  // This is the signal that there are no more real visited sites.

    titles->push_back(visited.title);
    urls->push_back(visited.url.spec());
  }
}

void OnMostVisitedURLsAvailable(
    ScopedJavaGlobalRef<jobject>* j_observer,
    int num_sites,
    const history::MostVisitedURLList& visited_list) {
  std::vector<base::string16> titles;
  std::vector<std::string> urls;
  ExtractMostVisitedTitlesAndURLs(visited_list, &titles, &urls, num_sites);

  JNIEnv* env = AttachCurrentThread();
  Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
      env,
      j_observer->obj(),
      ToJavaArrayOfStrings(env, titles).obj(),
      ToJavaArrayOfStrings(env, urls).obj());
}

SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
  scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(
      image_data.front(),
      image_data.size()));
  return image.get() ? *image : SkBitmap();
}

void OnObtainedThumbnail(
    ScopedJavaGlobalRef<jobject>* bitmap,
    ScopedJavaGlobalRef<jobject>* j_callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  JNIEnv* env = AttachCurrentThread();
  Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
      env, j_callback->obj(), bitmap->obj());
}

void GetUrlThumbnailTask(
    std::string url_string,
    scoped_refptr<TopSites> top_sites,
    ScopedJavaGlobalRef<jobject>* j_callback) {
  JNIEnv* env = AttachCurrentThread();

  ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
      new ScopedJavaGlobalRef<jobject>();

  GURL gurl(url_string);

  scoped_refptr<base::RefCountedMemory> data;
  if (top_sites->GetPageThumbnail(gurl, false, &data)) {
    SkBitmap thumbnail_bitmap = ExtractThumbnail(*data.get());
    if (!thumbnail_bitmap.empty()) {
      j_bitmap_ref->Reset(
          env,
          gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
    }
  }

  // Since j_callback is owned by this callback, when the callback falls out of
  // scope it will be deleted. We need to pass ownership to the next callback.
  ScopedJavaGlobalRef<jobject>* j_callback_pass =
      new ScopedJavaGlobalRef<jobject>(*j_callback);
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(
          &OnObtainedThumbnail,
          base::Owned(j_bitmap_ref), base::Owned(j_callback_pass)));
}

}  // namespace

MostVisitedSites::MostVisitedSites(Profile* profile)
    : profile_(profile), num_sites_(0), weak_ptr_factory_(this) {
}

MostVisitedSites::~MostVisitedSites() {
}

void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
  delete this;
}

void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
                                                  jobject obj,
                                                  jobject j_observer,
                                                  jint num_sites) {
  observer_.Reset(env, j_observer);
  num_sites_ = num_sites;

  QueryMostVisitedURLs();

  history::TopSites* top_sites = profile_->GetTopSites();
  if (top_sites) {
    // TopSites updates itself after a delay. To ensure up-to-date results,
    // force an update now.
    top_sites->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>(top_sites));
  }
}

// May be called from any thread
void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
                                       jobject obj,
                                       jstring url,
                                       jobject j_callback_obj) {
  ScopedJavaGlobalRef<jobject>* j_callback =
      new ScopedJavaGlobalRef<jobject>();
  j_callback->Reset(env, j_callback_obj);

  std::string url_string = ConvertJavaStringToUTF8(env, url);
  scoped_refptr<TopSites> top_sites(profile_->GetTopSites());
  BrowserThread::PostTask(
      BrowserThread::DB, FROM_HERE, base::Bind(
          &GetUrlThumbnailTask,
          url_string,
          top_sites, base::Owned(j_callback)));
}

void MostVisitedSites::BlacklistUrl(JNIEnv* env,
                                    jobject obj,
                                    jstring j_url) {
  TopSites* top_sites = profile_->GetTopSites();
  if (!top_sites)
    return;

  std::string url_string = ConvertJavaStringToUTF8(env, j_url);
  top_sites->AddBlacklistedURL(GURL(url_string));
}

void MostVisitedSites::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.
  QueryMostVisitedURLs();
}

// static
bool MostVisitedSites::Register(JNIEnv* env) {
  return RegisterNativesImpl(env);
}

void MostVisitedSites::QueryMostVisitedURLs() {
  SuggestionsServiceFactory* suggestions_service_factory =
      SuggestionsServiceFactory::GetInstance();
  SuggestionsService* suggestions_service =
      suggestions_service_factory->GetForProfile(profile_);
  if (suggestions_service) {
    // Suggestions service is enabled, initiate a query.
    suggestions_service->FetchSuggestionsData(
        base::Bind(
          &MostVisitedSites::OnSuggestionsProfileAvailable,
          weak_ptr_factory_.GetWeakPtr(),
          base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
  } else {
    InitiateTopSitesQuery();
  }
}

void MostVisitedSites::InitiateTopSitesQuery() {
  TopSites* top_sites = profile_->GetTopSites();
  if (!top_sites)
    return;

  top_sites->GetMostVisitedURLs(
      base::Bind(
          &OnMostVisitedURLsAvailable,
          base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
          num_sites_),
      false);
}

void MostVisitedSites::OnSuggestionsProfileAvailable(
    ScopedJavaGlobalRef<jobject>* j_observer,
    const SuggestionsProfile& suggestions_profile) {
  size_t size = suggestions_profile.suggestions_size();
  if (size == 0) {
    // No suggestions data available, initiate Top Sites query.
    InitiateTopSitesQuery();
    return;
  }

  std::vector<base::string16> titles;
  std::vector<std::string> urls;
  for (size_t i = 0; i < size; ++i) {
    const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
    titles.push_back(base::UTF8ToUTF16(suggestion.title()));
    urls.push_back(suggestion.url());
  }

  JNIEnv* env = AttachCurrentThread();
  Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
      env,
      j_observer->obj(),
      ToJavaArrayOfStrings(env, titles).obj(),
      ToJavaArrayOfStrings(env, urls).obj());
}

static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
  MostVisitedSites* most_visited_sites =
      new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));
  return reinterpret_cast<intptr_t>(most_visited_sites);
}

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