summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java122
-rw-r--r--chrome/browser/android/tab/thumbnail_tab_helper_android.cc161
2 files changed, 136 insertions, 147 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
index 440776c..2a50fce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java
@@ -5,21 +5,13 @@
package org.chromium.chrome.browser.tab;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
import android.os.Handler;
import android.text.TextUtils;
-import android.util.Log;
+import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.MathUtils;
-import org.chromium.content.browser.ContentReadbackHandler;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_public.browser.GestureStateListener;
import org.chromium.content_public.browser.WebContents;
@@ -39,13 +31,14 @@ public class ThumbnailTabHelper {
private final Tab mTab;
private final Handler mHandler;
- private final int mThumbnailWidth;
- private final int mThumbnailHeight;
+ private final int mThumbnailWidthDp;
+ private final int mThumbnailHeightDp;
private ContentViewCore mContentViewCore;
private boolean mThumbnailCapturedForLoad;
private boolean mIsRenderViewHostReady;
private boolean mWasRenderViewHostReady;
+ private String mRequestedUrl;
private final Runnable mThumbnailRunnable = new Runnable() {
@Override
@@ -61,33 +54,11 @@ public class ThumbnailTabHelper {
mThumbnailCapturedForLoad = !mTab.isHidden();
return;
}
- if (!shouldUpdateThumbnail()) return;
-
- int snapshotWidth = Math.min(mTab.getWidth(), mThumbnailWidth);
- int snapshotHeight = Math.min(mTab.getHeight(), mThumbnailHeight);
-
- ContentReadbackHandler readbackHandler = getActivity().getContentReadbackHandler();
- if (readbackHandler == null || mTab.getContentViewCore() == null) return;
- final String requestedUrl = mTab.getUrl();
- ContentReadbackHandler.GetBitmapCallback bitmapCallback =
- new ContentReadbackHandler.GetBitmapCallback() {
- @Override
- public void onFinishGetBitmap(Bitmap bitmap, int response) {
- // Ensure that the URLs match for the requested page, and ensure
- // that the page is still valid for thumbnail capturing (i.e.
- // not showing an error page).
- if (bitmap == null
- || !TextUtils.equals(requestedUrl, mTab.getUrl())
- || !mThumbnailCapturedForLoad
- || !canUpdateHistoryThumbnail()) {
- return;
- }
- updateHistoryThumbnail(bitmap);
- bitmap.recycle();
- }
- };
- readbackHandler.getContentBitmapAsync(1, new Rect(0, 0, snapshotWidth, snapshotHeight),
- mTab.getContentViewCore(), Bitmap.Config.ARGB_8888, bitmapCallback);
+ if (mTab.getWebContents() == null) return;
+
+ mRequestedUrl = mTab.getUrl();
+ nativeCaptureThumbnail(ThumbnailTabHelper.this, mTab.getWebContents(),
+ mThumbnailWidthDp, mThumbnailHeightDp);
}
};
@@ -196,7 +167,7 @@ public class ThumbnailTabHelper {
* @param tab The Tab whose thumbnails will be generated by this helper.
*/
public static void createForTab(Tab tab) {
- new ThumbnailTabHelper(tab);
+ if (!tab.isIncognito()) new ThumbnailTabHelper(tab);
}
/**
@@ -210,8 +181,11 @@ public class ThumbnailTabHelper {
mHandler = new Handler();
Resources res = tab.getWindowAndroid().getApplicationContext().getResources();
- mThumbnailWidth = res.getDimensionPixelSize(R.dimen.most_visited_thumbnail_width);
- mThumbnailHeight = res.getDimensionPixelSize(R.dimen.most_visited_thumbnail_height);
+ float density = res.getDisplayMetrics().density;
+ mThumbnailWidthDp = Math.round(
+ res.getDimension(R.dimen.most_visited_thumbnail_width) / density);
+ mThumbnailHeightDp = Math.round(
+ res.getDimension(R.dimen.most_visited_thumbnail_height) / density);
onContentChanged();
}
@@ -249,17 +223,6 @@ public class ThumbnailTabHelper {
mHandler.postDelayed(mThumbnailRunnable, THUMBNAIL_CAPTURE_DELAY_MS);
}
- private boolean shouldUpdateThumbnail() {
- return nativeShouldUpdateThumbnail(mTab.getProfile(), mTab.getUrl());
- }
-
- private void updateThumbnail(Bitmap bitmap) {
- if (mTab.getContentViewCore() != null) {
- final boolean atTop = mTab.getContentViewCore().computeVerticalScrollOffset() == 0;
- nativeUpdateThumbnail(mTab.getWebContents(), bitmap, atTop);
- }
- }
-
private boolean canUpdateHistoryThumbnail() {
String url = mTab.getUrl();
if (url.startsWith(UrlConstants.CHROME_SCHEME)
@@ -276,52 +239,17 @@ public class ThumbnailTabHelper {
&& mTab.getHeight() > 0;
}
- private void updateHistoryThumbnail(Bitmap bitmap) {
- if (mTab.isIncognito()) return;
-
- // TODO(yusufo): It will probably be faster and more efficient on resources to do this on
- // the native side, but the thumbnail_generator code has to be refactored a bit to allow
- // creating a downsized version of a bitmap progressively.
- if (bitmap.getWidth() != mThumbnailWidth
- || bitmap.getHeight() != mThumbnailHeight
- || bitmap.getConfig() != Config.ARGB_8888) {
- try {
- int[] dim = new int[] {
- bitmap.getWidth(), bitmap.getHeight()
- };
- // If the thumbnail size is small compared to the bitmap size downsize in
- // two stages. This makes the final quality better.
- float scale = Math.max(
- (float) mThumbnailWidth / dim[0],
- (float) mThumbnailHeight / dim[1]);
- int adjustedWidth = (scale < 1)
- ? mThumbnailWidth * (int) (1 / Math.sqrt(scale)) : mThumbnailWidth;
- int adjustedHeight = (scale < 1)
- ? mThumbnailHeight * (int) (1 / Math.sqrt(scale)) : mThumbnailHeight;
- scale = MathUtils.scaleToFitTargetSize(dim, adjustedWidth, adjustedHeight);
- // Horizontally center the source bitmap in the final result.
- float leftOffset = (adjustedWidth - dim[0]) / 2.0f / scale;
- Bitmap tmpBitmap = Bitmap.createBitmap(adjustedWidth,
- adjustedHeight, Config.ARGB_8888);
- Canvas c = new Canvas(tmpBitmap);
- c.scale(scale, scale);
- c.drawBitmap(bitmap, leftOffset, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
- if (scale < 1) {
- tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
- mThumbnailWidth, mThumbnailHeight, true);
- }
- updateThumbnail(tmpBitmap);
- tmpBitmap.recycle();
- } catch (OutOfMemoryError ex) {
- Log.w(TAG, "OutOfMemoryError while updating the history thumbnail.");
- }
- } else {
- updateThumbnail(bitmap);
- }
+ @CalledByNative
+ private boolean shouldSaveCapturedThumbnail() {
+ // Ensure that the URLs match for the requested page, and ensure
+ // that the page is still valid for thumbnail capturing (i.e.
+ // not showing an error page).
+ return TextUtils.equals(mRequestedUrl, mTab.getUrl())
+ && mThumbnailCapturedForLoad
+ && canUpdateHistoryThumbnail();
}
private static native void nativeInitThumbnailHelper(WebContents webContents);
- private static native void nativeUpdateThumbnail(
- WebContents webContents, Bitmap bitmap, boolean atTop);
- private static native boolean nativeShouldUpdateThumbnail(Profile profile, String url);
+ private static native void nativeCaptureThumbnail(ThumbnailTabHelper thumbnailTabHelper,
+ WebContents webContents, int thumbnailWidthDp, int thumbnailHeightDp);
}
diff --git a/chrome/browser/android/tab/thumbnail_tab_helper_android.cc b/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
index 17c0cae..c93d41c 100644
--- a/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
+++ b/chrome/browser/android/tab/thumbnail_tab_helper_android.cc
@@ -5,23 +5,108 @@
#include "chrome/browser/android/tab/thumbnail_tab_helper_android.h"
#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "chrome/browser/history/top_sites_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
+#include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
#include "chrome/browser/thumbnails/thumbnail_service.h"
#include "chrome/browser/thumbnails/thumbnail_service_factory.h"
#include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
-#include "components/history/core/browser/top_sites.h"
+#include "chrome/browser/thumbnails/thumbnailing_algorithm.h"
+#include "chrome/browser/thumbnails/thumbnailing_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "jni/ThumbnailTabHelper_jni.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
+using thumbnails::ThumbnailingAlgorithm;
+using thumbnails::ThumbnailingContext;
+using thumbnails::ThumbnailService;
+
+namespace {
+
+const int kScrollbarWidthDp = 6;
+
+void UpdateThumbnail(const ThumbnailingContext& context,
+ const SkBitmap& thumbnail) {
+ gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail);
+ context.service->SetPageThumbnail(context, image);
+}
+
+void ProcessCapturedBitmap(
+ const base::android::ScopedJavaGlobalRef<jobject>& jthumbnail_tab_helper,
+ scoped_refptr<ThumbnailingContext> context,
+ scoped_refptr<ThumbnailingAlgorithm> algorithm,
+ const SkBitmap& bitmap,
+ content::ReadbackResponse response) {
+ if (response != content::READBACK_SUCCESS)
+ return;
+
+ // On success, we must be on the UI thread (on failure because of shutdown we
+ // are not on the UI thread).
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ if (!Java_ThumbnailTabHelper_shouldSaveCapturedThumbnail(
+ env, jthumbnail_tab_helper.obj())) {
+ return;
+ }
+
+ algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
+}
+
+void CaptureThumbnailInternal(
+ const base::android::ScopedJavaGlobalRef<jobject>& jthumbnail_tab_helper,
+ content::WebContents* web_contents,
+ scoped_refptr<ThumbnailingContext> context,
+ scoped_refptr<ThumbnailingAlgorithm> algorithm,
+ const gfx::Size& thumbnail_size) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ content::RenderWidgetHost* render_widget_host =
+ web_contents->GetRenderViewHost();
+ content::RenderWidgetHostView* view = render_widget_host->GetView();
+ if (!view)
+ return;
+ if (!view->IsSurfaceAvailableForCopy())
+ return;
+
+ gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
+ // Clip the pixels that will commonly hold a scrollbar, which looks bad in
+ // thumbnails.
+ copy_rect.Inset(0, 0, kScrollbarWidthDp, 0);
+ if (copy_rect.IsEmpty())
+ return;
+
+ ui::ScaleFactor scale_factor =
+ ui::GetSupportedScaleFactor(
+ ui::GetScaleFactorForNativeView(view->GetNativeView()));
+ context->clip_result = algorithm->GetCanvasCopyInfo(
+ copy_rect.size(),
+ scale_factor,
+ &copy_rect,
+ &context->requested_copy_size);
+
+ // Workaround for a bug where CopyFromBackingStore() accepts different input
+ // units on Android (DIP) vs on other platforms (pixels).
+ // TODO(newt): remove this line once https://crbug.com/540497 is fixed.
+ context->requested_copy_size = thumbnail_size;
+
+ render_widget_host->CopyFromBackingStore(
+ copy_rect, context->requested_copy_size,
+ base::Bind(&ProcessCapturedBitmap, jthumbnail_tab_helper, context,
+ algorithm),
+ kN32_SkColorType);
+}
+
+} // namespace
+
// static
bool RegisterThumbnailTabHelperAndroid(JNIEnv* env) {
return RegisterNativesImpl(env);
@@ -44,57 +129,33 @@ static void InitThumbnailHelper(JNIEnv* env,
thumbnail_tab_helper->set_enabled(false);
}
-static jboolean ShouldUpdateThumbnail(JNIEnv* env,
- const JavaParamRef<jclass>& clazz,
- const JavaParamRef<jobject>& jprofile,
- const JavaParamRef<jstring>& jurl) {
- Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
-
- GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
- scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
- ThumbnailServiceFactory::GetForProfile(profile);
- return (thumbnail_service.get() != NULL &&
- thumbnail_service->ShouldAcquirePageThumbnail(url));
-}
-
-static void UpdateThumbnail(JNIEnv* env,
- const JavaParamRef<jclass>& clazz,
- const JavaParamRef<jobject>& jweb_contents,
- const JavaParamRef<jobject>& bitmap,
- jboolean jat_top) {
+static void CaptureThumbnail(JNIEnv* env,
+ const JavaParamRef<jclass>& clazz,
+ const JavaParamRef<jobject>& jthumbnail_tab_helper,
+ const JavaParamRef<jobject>& jweb_contents,
+ jint thumbnail_width_dp,
+ jint thumbnail_height_dp) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(jweb_contents);
DCHECK(web_contents);
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
- Profile* profile = Profile::FromBrowserContext(
- web_contents->GetBrowserContext());
-
- gfx::JavaBitmap bitmap_lock(bitmap);
- SkBitmap sk_bitmap;
- gfx::Size size = bitmap_lock.size();
- SkColorType color_type = kN32_SkColorType;
- sk_bitmap.setInfo(
- SkImageInfo::Make(size.width(), size.height(),
- color_type, kPremul_SkAlphaType), 0);
- sk_bitmap.setPixels(bitmap_lock.pixels());
-
- // TODO(nileshagrawal): Refactor this.
- // We were using some non-public methods from ThumbnailTabHelper. We need to
- // either add android specific logic to ThumbnailTabHelper or create our own
- // helper which is driven by the java app (will need to pull out some logic
- // from ThumbnailTabHelper to a common class).
- scoped_refptr<history::TopSites> ts = TopSitesFactory::GetForProfile(profile);
- if (!ts)
+ scoped_refptr<ThumbnailService> thumbnail_service =
+ ThumbnailServiceFactory::GetForProfile(profile);
+ if (thumbnail_service.get() == nullptr ||
+ !thumbnail_service->ShouldAcquirePageThumbnail(
+ web_contents->GetLastCommittedURL())) {
return;
+ }
- // Compute the thumbnail score.
- ThumbnailScore score;
- score.at_top = jat_top;
- score.boring_score = color_utils::CalculateBoringScore(sk_bitmap);
- score.good_clipping = true;
- score.load_completed = !web_contents->IsLoading();
+ const gfx::Size thumbnail_size(thumbnail_width_dp, thumbnail_height_dp);
+ scoped_refptr<ThumbnailingAlgorithm> algorithm(
+ new thumbnails::SimpleThumbnailCrop(thumbnail_size));
- gfx::Image image = gfx::Image::CreateFrom1xBitmap(sk_bitmap);
- const GURL& url = web_contents->GetURL();
- ts->SetPageThumbnail(url, image, score);
+ scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
+ web_contents, thumbnail_service.get(), false /*load_interrupted*/));
+ CaptureThumbnailInternal(
+ base::android::ScopedJavaGlobalRef<jobject>(env, jthumbnail_tab_helper),
+ web_contents, context, algorithm, thumbnail_size);
}