Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / android / most_visited_sites.cc
index 5016cb4..e13597c 100644 (file)
@@ -4,17 +4,31 @@
 
 #include "chrome/browser/android/most_visited_sites.h"
 
+#include <string>
+#include <vector>
+
 #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/callback.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.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/suggestions_service_factory.h"
+#include "chrome/browser/search/suggestions/suggestions_source.h"
+#include "chrome/browser/thumbnails/thumbnail_list_source.h"
+#include "components/suggestions/suggestions_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/url_data_source.h"
 #include "jni/MostVisitedSites_jni.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -28,9 +42,42 @@ 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 {
 
+// Total number of tiles displayed.
+const char kNumTilesHistogramName[] = "NewTabPage.NumberOfTiles";
+// Tracking thumbnails.
+const char kNumLocalThumbnailTilesHistogramName[] =
+    "NewTabPage.NumberOfThumbnailTiles";
+const char kNumEmptyTilesHistogramName[] = "NewTabPage.NumberOfGrayTiles";
+const char kNumServerTilesHistogramName[] = "NewTabPage.NumberOfExternalTiles";
+// Client suggestion opened.
+const char kOpenedItemClientHistogramName[] = "NewTabPage.MostVisited.client";
+// Control group suggestion opened.
+const char kOpenedItemControlHistogramName[] = "NewTabPage.MostVisited.client0";
+// Server suggestion opened, no provider.
+const char kOpenedItemServerHistogramName[] = "NewTabPage.MostVisited.server";
+// Server suggestion opened with provider.
+const char kOpenedItemServerProviderHistogramFormat[] =
+    "NewTabPage.MostVisited.server%d";
+// Client impression.
+const char kImpressionClientHistogramName[] =
+    "NewTabPage.SuggestionsImpression.client";
+// Control group impression.
+const char kImpressionControlHistogramName[] =
+    "NewTabPage.SuggestionsImpression.client0";
+// Server suggestion impression, no provider.
+const char kImpressionServerHistogramName[] =
+    "NewTabPage.SuggestionsImpression.server";
+// Server suggestion impression with provider.
+const char kImpressionServerHistogramFormat[] =
+    "NewTabPage.SuggestionsImpression.server%d";
+
 void ExtractMostVisitedTitlesAndURLs(
     const history::MostVisitedURLList& visited_list,
     std::vector<base::string16>* titles,
@@ -48,22 +95,6 @@ void ExtractMostVisitedTitlesAndURLs(
   }
 }
 
-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(),
@@ -71,19 +102,19 @@ SkBitmap ExtractThumbnail(const base::RefCountedMemory& image_data) {
   return image.get() ? *image : SkBitmap();
 }
 
-void OnObtainedThumbnail(
-    ScopedJavaGlobalRef<jobject>* bitmap,
-    ScopedJavaGlobalRef<jobject>* j_callback) {
+void AddForcedURLOnUIThread(scoped_refptr<history::TopSites> top_sites,
+                            const GURL& url) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  JNIEnv* env = AttachCurrentThread();
-  Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
-      env, j_callback->obj(), bitmap->obj());
+  top_sites->AddForcedURL(url, base::Time::Now());
 }
 
+// Runs on the DB thread.
 void GetUrlThumbnailTask(
     std::string url_string,
     scoped_refptr<TopSites> top_sites,
-    ScopedJavaGlobalRef<jobject>* j_callback) {
+    ScopedJavaGlobalRef<jobject>* j_callback,
+    MostVisitedSites::LookupSuccessCallback lookup_success_ui_callback,
+    base::Closure lookup_failed_ui_callback) {
   JNIEnv* env = AttachCurrentThread();
 
   ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
@@ -99,6 +130,20 @@ void GetUrlThumbnailTask(
           env,
           gfx::ConvertToJavaBitmap(&thumbnail_bitmap).obj());
     }
+  } else {
+    // A thumbnail is not locally available for |gurl|. Make sure it is put in
+    // the list to be fetched at the next visit to this site.
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(AddForcedURLOnUIThread, top_sites, gurl));
+
+    // If appropriate, return on the UI thread to execute the proper callback.
+    if (!lookup_failed_ui_callback.is_null()) {
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE, lookup_failed_ui_callback);
+      delete j_bitmap_ref;
+      return;
+    }
   }
 
   // Since j_callback is owned by this callback, when the callback falls out of
@@ -107,15 +152,36 @@ void GetUrlThumbnailTask(
       new ScopedJavaGlobalRef<jobject>(*j_callback);
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(
-          &OnObtainedThumbnail,
-          base::Owned(j_bitmap_ref), base::Owned(j_callback_pass)));
+      base::Bind(lookup_success_ui_callback, base::Owned(j_bitmap_ref),
+                 base::Owned(j_callback_pass)));
+}
+
+// Log an event for a given |histogram| at a given element |position|. This
+// routine exists because regular histogram macros are cached thus can't be used
+// if the name of the histogram will change at a given call site.
+void LogHistogramEvent(const std::string& histogram, int position,
+                       int num_sites) {
+  base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
+      histogram,
+      1,
+      num_sites,
+      num_sites + 1,
+      base::Histogram::kUmaTargetedHistogramFlag);
+  if (counter)
+    counter->Add(position);
 }
 
 }  // namespace
 
 MostVisitedSites::MostVisitedSites(Profile* profile)
-    : profile_(profile), num_sites_(0) {
+    : profile_(profile), num_sites_(0), is_control_group_(false),
+      num_local_thumbs_(0), num_server_thumbs_(0), num_empty_thumbs_(0),
+      weak_ptr_factory_(this) {
+  // Register the debugging page for the Suggestions Service and the thumbnails
+  // debugging page.
+  content::URLDataSource::Add(profile_,
+                              new suggestions::SuggestionsSource(profile_));
+  content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
 }
 
 MostVisitedSites::~MostVisitedSites() {
@@ -125,6 +191,10 @@ void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
   delete this;
 }
 
+void MostVisitedSites::OnLoadingComplete(JNIEnv* env, jobject obj) {
+  RecordUMAMetrics();
+}
+
 void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
                                                   jobject obj,
                                                   jobject j_observer,
@@ -147,33 +217,93 @@ void MostVisitedSites::SetMostVisitedURLsObserver(JNIEnv* env,
   }
 }
 
-// May be called from any thread
+// Called from the UI Thread.
 void MostVisitedSites::GetURLThumbnail(JNIEnv* env,
                                        jobject obj,
                                        jstring url,
                                        jobject j_callback_obj) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   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());
+
+  // If the Suggestions service is enabled, create a callback to fetch a
+  // server thumbnail from it, in case the local thumbnail is not found.
+  SuggestionsService* suggestions_service =
+      SuggestionsServiceFactory::GetForProfile(profile_);
+  base::Closure lookup_failed_callback = suggestions_service ?
+      base::Bind(&MostVisitedSites::GetSuggestionsThumbnailOnUIThread,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 suggestions_service, url_string,
+                 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))) :
+      base::Closure();
+  LookupSuccessCallback lookup_success_callback =
+      base::Bind(&MostVisitedSites::OnObtainedThumbnail,
+                 weak_ptr_factory_.GetWeakPtr());
+
   BrowserThread::PostTask(
-      BrowserThread::DB, FROM_HERE, base::Bind(
-          &GetUrlThumbnailTask,
-          url_string,
-          top_sites, base::Owned(j_callback)));
+      BrowserThread::DB, FROM_HERE,
+          base::Bind(
+              &GetUrlThumbnailTask, url_string, top_sites,
+              base::Owned(j_callback), lookup_success_callback,
+              lookup_failed_callback));
 }
 
 void MostVisitedSites::BlacklistUrl(JNIEnv* env,
                                     jobject obj,
                                     jstring j_url) {
-  TopSites* top_sites = profile_->GetTopSites();
-  if (!top_sites)
-    return;
+  std::string url = ConvertJavaStringToUTF8(env, j_url);
+
+  switch (mv_source_) {
+    case TOP_SITES: {
+      TopSites* top_sites = profile_->GetTopSites();
+      DCHECK(top_sites);
+      top_sites->AddBlacklistedURL(GURL(url));
+      break;
+    }
 
-  std::string url_string = ConvertJavaStringToUTF8(env, j_url);
-  top_sites->AddBlacklistedURL(GURL(url_string));
+    case SUGGESTIONS_SERVICE: {
+      SuggestionsService* suggestions_service =
+          SuggestionsServiceFactory::GetForProfile(profile_);
+      DCHECK(suggestions_service);
+      suggestions_service->BlacklistURL(
+          GURL(url),
+          base::Bind(
+              &MostVisitedSites::OnSuggestionsProfileAvailable,
+              weak_ptr_factory_.GetWeakPtr(),
+              base::Owned(new ScopedJavaGlobalRef<jobject>(observer_))));
+      break;
+    }
+  }
+}
+
+void MostVisitedSites::RecordOpenedMostVisitedItem(JNIEnv* env,
+                                                   jobject obj,
+                                                   jint index) {
+  switch (mv_source_) {
+    case TOP_SITES: {
+      const std::string histogram = is_control_group_ ?
+          kOpenedItemControlHistogramName : kOpenedItemClientHistogramName;
+      LogHistogramEvent(histogram, index, num_sites_);
+      break;
+    }
+    case SUGGESTIONS_SERVICE: {
+      if (server_suggestions_.suggestions_size() > index) {
+        if (server_suggestions_.suggestions(index).providers_size()) {
+          std::string histogram = base::StringPrintf(
+              kOpenedItemServerProviderHistogramFormat,
+              server_suggestions_.suggestions(index).providers(0));
+          LogHistogramEvent(histogram, index, num_sites_);
+        } else {
+          UMA_HISTOGRAM_SPARSE_SLOWLY(kOpenedItemServerHistogramName, index);
+        }
+      }
+      break;
+    }
+  }
 }
 
 void MostVisitedSites::Observe(int type,
@@ -181,8 +311,10 @@ void MostVisitedSites::Observe(int type,
                                const content::NotificationDetails& details) {
   DCHECK_EQ(type, chrome::NOTIFICATION_TOP_SITES_CHANGED);
 
-  // Most visited urls changed, query again.
-  QueryMostVisitedURLs();
+  if (mv_source_ == TOP_SITES) {
+    // The displayed suggestions are invalidated.
+    QueryMostVisitedURLs();
+  }
 }
 
 // static
@@ -191,18 +323,163 @@ bool MostVisitedSites::Register(JNIEnv* env) {
 }
 
 void MostVisitedSites::QueryMostVisitedURLs() {
+  SuggestionsService* suggestions_service =
+      SuggestionsServiceFactory::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,
+          &MostVisitedSites::OnMostVisitedURLsAvailable,
+          weak_ptr_factory_.GetWeakPtr(),
           base::Owned(new ScopedJavaGlobalRef<jobject>(observer_)),
           num_sites_),
       false);
 }
 
+void MostVisitedSites::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);
+
+  mv_source_ = TOP_SITES;
+
+  int num_tiles = urls.size();
+  UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, num_tiles);
+  const std::string histogram = is_control_group_ ?
+      kImpressionControlHistogramName : kImpressionClientHistogramName;
+  for (int i = 0; i < num_tiles; ++i) {
+    LogHistogramEvent(histogram, i, num_sites_);
+  }
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
+      env,
+      j_observer->obj(),
+      ToJavaArrayOfStrings(env, titles).obj(),
+      ToJavaArrayOfStrings(env, urls).obj());
+}
+
+void MostVisitedSites::OnSuggestionsProfileAvailable(
+    ScopedJavaGlobalRef<jobject>* j_observer,
+    const SuggestionsProfile& suggestions_profile) {
+  int size = suggestions_profile.suggestions_size();
+
+  // Determine if the user is in a control group (they would have received
+  // suggestions, but are in a group where they shouldn't).
+  is_control_group_ = size && SuggestionsService::IsControlGroup();
+
+  // If no suggestions data is available or the user is in a control group,
+  // initiate Top Sites query.
+  if (is_control_group_ || !size) {
+    InitiateTopSitesQuery();
+    return;
+  }
+
+  std::vector<base::string16> titles;
+  std::vector<std::string> urls;
+
+  int i = 0;
+  for (; i < size && i < num_sites_; ++i) {
+    const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
+    titles.push_back(base::UTF8ToUTF16(suggestion.title()));
+    urls.push_back(suggestion.url());
+    if (suggestion.providers_size()) {
+      std::string histogram = base::StringPrintf(
+          kImpressionServerHistogramFormat, suggestion.providers(0));
+      LogHistogramEvent(histogram, i, num_sites_);
+    } else {
+      UMA_HISTOGRAM_SPARSE_SLOWLY(kImpressionServerHistogramName, i);
+    }
+  }
+  UMA_HISTOGRAM_SPARSE_SLOWLY(kNumTilesHistogramName, i);
+
+  mv_source_ = SUGGESTIONS_SERVICE;
+  // Keep a copy of the suggestions for eventual logging.
+  server_suggestions_ = suggestions_profile;
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable(
+      env,
+      j_observer->obj(),
+      ToJavaArrayOfStrings(env, titles).obj(),
+      ToJavaArrayOfStrings(env, urls).obj());
+}
+
+void MostVisitedSites::OnObtainedThumbnail(
+    ScopedJavaGlobalRef<jobject>* bitmap,
+    ScopedJavaGlobalRef<jobject>* j_callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  JNIEnv* env = AttachCurrentThread();
+  if (bitmap->obj()) {
+    num_local_thumbs_++;
+  } else {
+    num_empty_thumbs_++;
+  }
+  Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
+      env, j_callback->obj(), bitmap->obj());
+}
+
+void MostVisitedSites::GetSuggestionsThumbnailOnUIThread(
+    SuggestionsService* suggestions_service,
+    const std::string& url_string,
+    ScopedJavaGlobalRef<jobject>* j_callback) {
+  suggestions_service->GetPageThumbnail(
+      GURL(url_string),
+      base::Bind(&MostVisitedSites::OnSuggestionsThumbnailAvailable,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::Owned(new ScopedJavaGlobalRef<jobject>(*j_callback))));
+}
+
+void MostVisitedSites::OnSuggestionsThumbnailAvailable(
+    ScopedJavaGlobalRef<jobject>* j_callback,
+    const GURL& url,
+    const SkBitmap* bitmap) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  JNIEnv* env = AttachCurrentThread();
+
+  ScopedJavaGlobalRef<jobject>* j_bitmap_ref =
+      new ScopedJavaGlobalRef<jobject>();
+  if (bitmap) {
+    num_server_thumbs_++;
+    j_bitmap_ref->Reset(
+        env,
+        gfx::ConvertToJavaBitmap(bitmap).obj());
+  } else {
+    num_empty_thumbs_++;
+  }
+
+  Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable(
+      env, j_callback->obj(), j_bitmap_ref->obj());
+}
+
+void MostVisitedSites::RecordUMAMetrics() {
+  UMA_HISTOGRAM_SPARSE_SLOWLY(kNumLocalThumbnailTilesHistogramName,
+                              num_local_thumbs_);
+  num_local_thumbs_ = 0;
+  UMA_HISTOGRAM_SPARSE_SLOWLY(kNumEmptyTilesHistogramName, num_empty_thumbs_);
+  num_empty_thumbs_ = 0;
+  UMA_HISTOGRAM_SPARSE_SLOWLY(kNumServerTilesHistogramName, num_server_thumbs_);
+  num_server_thumbs_ = 0;
+}
+
 static jlong Init(JNIEnv* env, jobject obj, jobject jprofile) {
   MostVisitedSites* most_visited_sites =
       new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile));