summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc3
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc2
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h5
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc7
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.cc169
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator.h49
-rw-r--r--chrome/browser/tab_contents/thumbnail_generator_unittest.cc100
-rw-r--r--chrome/common/chrome_switches.cc5
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/render_messages_params.cc2
-rw-r--r--chrome/common/render_messages_params.h3
-rw-r--r--chrome/renderer/render_view.cc13
-rw-r--r--chrome/renderer/render_view.h1
-rw-r--r--chrome/renderer/render_widget.cc6
-rw-r--r--chrome/renderer/render_widget.h4
15 files changed, 348 insertions, 22 deletions
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 26b3f4c..b9918e1 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -729,7 +729,8 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer(
switches::kPpapiOutOfProcess,
switches::kEnablePrintPreview,
switches::kEnableCrxlessWebApps,
- switches::kDisable3DAPIs
+ switches::kDisable3DAPIs,
+ switches::kEnableInBrowserThumbnailing,
};
renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
arraysize(kSwitchNames));
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index 4dae42f..b97923f 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -815,6 +815,8 @@ void RenderWidgetHost::OnMsgUpdateRect(
// Update our knowledge of the RenderWidget's size.
current_size_ = params.view_size;
+ // Update our knowledge of the RenderWidget's scroll offset.
+ last_scroll_offset_ = params.scroll_offset;
bool is_resize_ack =
ViewHostMsg_UpdateRect_Flags::is_resize_ack(params.flags);
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index 2eda8cd..bfefefa 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -393,6 +393,8 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// Activate deferred plugin handles.
void ActivateDeferredPluginHandles();
+ const gfx::Size& last_scroll_offset() const { return last_scroll_offset_; }
+
protected:
// Internal implementation of the public Forward*Event() methods.
void ForwardInputEvent(const WebKit::WebInputEvent& input_event,
@@ -665,6 +667,9 @@ class RenderWidgetHost : public IPC::Channel::Listener,
std::vector<gfx::PluginWindowHandle> deferred_plugin_handles_;
+ // The last scroll offset of the render widget.
+ gfx::Size last_scroll_offset_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHost);
};
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 2f975a7d..d852134 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -74,6 +74,7 @@
#include "chrome/browser/tab_contents/tab_contents_delegate.h"
#include "chrome/browser/tab_contents/tab_contents_ssl_helper.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/browser/tab_contents/thumbnail_generator.h"
#include "chrome/browser/tab_contents/web_navigation_observer.h"
#include "chrome/browser/translate/page_translated_details.h"
#include "chrome/browser/ui/app_modal_dialogs/message_box_handler.h"
@@ -2378,6 +2379,12 @@ void TabContents::OnPageContents(const GURL& url,
NotificationType::TAB_LANGUAGE_DETERMINED,
Source<TabContents>(this),
Details<std::string>(&lang));
+
+ // Generate the thumbnail here if the in-browser thumbnailing is enabled.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableInBrowserThumbnailing)) {
+ ThumbnailGenerator::UpdateThumbnailIfNecessary(this, url);
+ }
}
void TabContents::OnPageTranslated(int32 page_id,
diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc
index c370719..39d61f7 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.cc
+++ b/chrome/browser/tab_contents/thumbnail_generator.cc
@@ -11,14 +11,22 @@
#include "base/scoped_ptr.h"
#include "base/time.h"
#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/history/top_sites.h"
+#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/backing_store.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/property_bag.h"
+#include "chrome/common/thumbnail_score.h"
+#include "gfx/color_utils.h"
#include "gfx/rect.h"
#include "gfx/skbitmap_operations.h"
+#include "googleurl/src/gurl.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -99,9 +107,12 @@ WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) {
// Creates a downsampled thumbnail for the given backing store. The returned
// bitmap will be isNull if there was an error creating it.
-SkBitmap GetBitmapForBackingStore(BackingStore* backing_store,
- int desired_width,
- int desired_height) {
+SkBitmap GetBitmapForBackingStore(
+ BackingStore* backing_store,
+ int desired_width,
+ int desired_height,
+ int options,
+ ThumbnailGenerator::ClipResult* clip_result) {
base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
SkBitmap result;
@@ -115,20 +126,31 @@ SkBitmap GetBitmapForBackingStore(BackingStore* backing_store,
return result;
const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false);
- // Need to resize it to the size we want, so downsample until it's
- // close, and let the caller make it the exact size if desired.
- result = SkBitmapOperations::DownsampleByTwoUntilSize(
- bmp, desired_width, desired_height);
-
- // This is a bit subtle. SkBitmaps are refcounted, but the magic
- // ones in PlatformCanvas can't be assigned to SkBitmap with proper
- // refcounting. If the bitmap doesn't change, then the downsampler
- // will return the input bitmap, which will be the reference to the
- // weird PlatformCanvas one insetad of a regular one. To get a
- // regular refcounted bitmap, we need to copy it.
- if (bmp.width() == result.width() &&
- bmp.height() == result.height())
- bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
+ // Check if a clipped thumbnail is requested.
+ if (options & ThumbnailGenerator::kClippedThumbnail) {
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
+ bmp, desired_width, desired_height, clip_result);
+
+ // Need to resize it to the size we want, so downsample until it's
+ // close, and let the caller make it the exact size if desired.
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(
+ clipped_bitmap, desired_width, desired_height);
+ } else {
+ // Need to resize it to the size we want, so downsample until it's
+ // close, and let the caller make it the exact size if desired.
+ result = SkBitmapOperations::DownsampleByTwoUntilSize(
+ bmp, desired_width, desired_height);
+
+ // This is a bit subtle. SkBitmaps are refcounted, but the magic
+ // ones in PlatformCanvas can't be assigned to SkBitmap with proper
+ // refcounting. If the bitmap doesn't change, then the downsampler
+ // will return the input bitmap, which will be the reference to the
+ // weird PlatformCanvas one insetad of a regular one. To get a
+ // regular refcounted bitmap, we need to copy it.
+ if (bmp.width() == result.width() &&
+ bmp.height() == result.height())
+ bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
+ }
HISTOGRAM_TIMES(kThumbnailHistogramName,
base::TimeTicks::Now() - begin_compute_thumbnail);
@@ -223,7 +245,9 @@ void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer,
// we'll go with it.
SkBitmap first_try = GetBitmapForBackingStore(backing_store,
desired_size.width(),
- desired_size.height());
+ desired_size.height(),
+ kNoOptions,
+ NULL);
callback->Run(first_try);
delete callback;
@@ -275,6 +299,13 @@ void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer,
SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
RenderWidgetHost* renderer) const {
+ return GetThumbnailForRendererWithOptions(renderer, kNoOptions, NULL);
+}
+
+SkBitmap ThumbnailGenerator::GetThumbnailForRendererWithOptions(
+ RenderWidgetHost* renderer,
+ int options,
+ ClipResult* clip_result) const {
WidgetThumbnail* wt = GetDataForHost(renderer);
BackingStore* backing_store = renderer->GetBackingStore(false);
@@ -299,7 +330,9 @@ SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
// invalidated on the next paint.
wt->thumbnail = GetBitmapForBackingStore(backing_store,
kThumbnailWidth,
- kThumbnailHeight);
+ kThumbnailHeight,
+ options,
+ clip_result);
return wt->thumbnail;
}
@@ -320,7 +353,9 @@ void ThumbnailGenerator::WidgetWillDestroyBackingStore(
// an existing thumbnail.
SkBitmap new_thumbnail = GetBitmapForBackingStore(backing_store,
kThumbnailWidth,
- kThumbnailHeight);
+ kThumbnailHeight,
+ kNoOptions,
+ NULL);
if (!new_thumbnail.isNull())
wt->thumbnail = new_thumbnail;
}
@@ -534,3 +569,97 @@ void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) {
if (found != shown_hosts_.end())
shown_hosts_.erase(found);
}
+
+double ThumbnailGenerator::CalculateBoringScore(SkBitmap* bitmap) {
+ if (bitmap->isNull() || bitmap->empty())
+ return 1.0;
+ int histogram[256] = {0};
+ color_utils::BuildLumaHistogram(bitmap, histogram);
+
+ int color_count = *std::max_element(histogram, histogram + 256);
+ int pixel_count = bitmap->width() * bitmap->height();
+ return static_cast<double>(color_count) / pixel_count;
+}
+
+SkBitmap ThumbnailGenerator::GetClippedBitmap(const SkBitmap& bitmap,
+ int desired_width,
+ int desired_height,
+ ClipResult* clip_result) {
+ const SkRect dest_rect = { 0, 0,
+ SkIntToScalar(desired_width),
+ SkIntToScalar(desired_height) };
+ const float dest_aspect = dest_rect.width() / dest_rect.height();
+
+ // Get the src rect so that we can preserve the aspect ratio while filling
+ // the destination.
+ SkIRect src_rect;
+ if (bitmap.width() < dest_rect.width() ||
+ bitmap.height() < dest_rect.height()) {
+ // Source image is smaller: we clip the part of source image within the
+ // dest rect, and then stretch it to fill the dest rect. We don't respect
+ // the aspect ratio in this case.
+ src_rect.set(0, 0, static_cast<S16CPU>(dest_rect.width()),
+ static_cast<S16CPU>(dest_rect.height()));
+ if (clip_result)
+ *clip_result = ThumbnailGenerator::kSourceIsSmaller;
+ } else {
+ const float src_aspect =
+ static_cast<float>(bitmap.width()) / bitmap.height();
+ if (src_aspect > dest_aspect) {
+ // Wider than tall, clip horizontally: we center the smaller
+ // thumbnail in the wider screen.
+ S16CPU new_width = static_cast<S16CPU>(bitmap.height() * dest_aspect);
+ S16CPU x_offset = (bitmap.width() - new_width) / 2;
+ src_rect.set(x_offset, 0, new_width + x_offset, bitmap.height());
+ if (clip_result)
+ *clip_result = ThumbnailGenerator::kWiderThanTall;
+ } else if (src_aspect < dest_aspect) {
+ src_rect.set(0, 0, bitmap.width(),
+ static_cast<S16CPU>(bitmap.width() / dest_aspect));
+ if (clip_result)
+ *clip_result = ThumbnailGenerator::kTallerThanWide;
+ } else {
+ src_rect.set(0, 0, bitmap.width(), bitmap.height());
+ if (clip_result)
+ *clip_result = ThumbnailGenerator::kNotClipped;
+ }
+ }
+
+ SkBitmap clipped_bitmap;
+ bitmap.extractSubset(&clipped_bitmap, src_rect);
+ return clipped_bitmap;
+}
+
+void ThumbnailGenerator::UpdateThumbnailIfNecessary(
+ TabContents* tab_contents, const GURL& url) {
+ if (tab_contents->profile()->IsOffTheRecord() ||
+ (url.SchemeIs("chrome") && url.host() == "newtab"))
+ return;
+ // TODO(satorux): Add more conditions here to avoid unnecessary
+ // thumbnail generation.
+
+ ThumbnailGenerator* generator = g_browser_process->GetThumbnailGenerator();
+ const int options = ThumbnailGenerator::kClippedThumbnail;
+ ThumbnailGenerator::ClipResult clip_result = ThumbnailGenerator::kNotClipped;
+ SkBitmap thumbnail = generator->GetThumbnailForRendererWithOptions(
+ tab_contents->render_view_host(), options, &clip_result);
+ // Failed to generate a thumbnail. Maybe the tab is in the background?
+ if (thumbnail.isNull())
+ return;
+
+ // Compute the thumbnail score.
+ ThumbnailScore score;
+ score.at_top =
+ (tab_contents->render_view_host()->last_scroll_offset().height() == 0);
+ score.boring_score = ThumbnailGenerator::CalculateBoringScore(&thumbnail);
+ score.good_clipping =
+ (clip_result == ThumbnailGenerator::kTallerThanWide ||
+ clip_result == ThumbnailGenerator::kNotClipped);
+
+ history::TopSites* top_sites = tab_contents->profile()->GetTopSites();
+ top_sites->SetPageThumbnail(url, thumbnail, score);
+ VLOG(1) << "Thumbnail taken for " << url
+ << ", at_top: " << score.at_top
+ << ", boring_score: " << score.boring_score
+ << ", good_clipping: " << score.good_clipping;
+}
diff --git a/chrome/browser/tab_contents/thumbnail_generator.h b/chrome/browser/tab_contents/thumbnail_generator.h
index 1952b53..de57c21 100644
--- a/chrome/browser/tab_contents/thumbnail_generator.h
+++ b/chrome/browser/tab_contents/thumbnail_generator.h
@@ -18,6 +18,7 @@
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
+class GURL;
class RenderWidgetHost;
class SkBitmap;
class TabContents;
@@ -25,6 +26,27 @@ class TabContents;
class ThumbnailGenerator : NotificationObserver {
public:
typedef Callback1<const SkBitmap&>::Type ThumbnailReadyCallback;
+ // The result of clipping. This can be used to determine if the
+ // generated thumbnail is good or not.
+ enum ClipResult {
+ // The source image is smaller.
+ kSourceIsSmaller,
+ // Wider than tall, clip horizontally.
+ kWiderThanTall,
+ // Taller than wide, clip vertically.
+ kTallerThanWide,
+ // The source and destination aspect ratios are identical.
+ kNotClipped,
+ };
+
+ // Bitmasks of options for generating a thumbnail.
+ enum ThumbnailOptions {
+ // No options.
+ kNoOptions = 0,
+ // Request a clipped thumbnail with the aspect ratio preserved.
+ kClippedThumbnail = 1 << 0,
+ };
+
// This class will do nothing until you call StartThumbnailing.
ThumbnailGenerator();
~ThumbnailGenerator();
@@ -60,6 +82,13 @@ class ThumbnailGenerator : NotificationObserver {
// renderer.
SkBitmap GetThumbnailForRenderer(RenderWidgetHost* renderer) const;
+ // This returns a thumbnail of a fixed, small size for the given
+ // renderer. |options| is a bitmask of ThumbnailOptions. If
+ // |clip_result| is non-NULL, the result of clipping will be written.
+ SkBitmap GetThumbnailForRendererWithOptions(RenderWidgetHost* renderer,
+ int options,
+ ClipResult* clip_result) const;
+
// Start or stop monitoring notifications for |renderer| based on the value
// of |monitor|.
void MonitorRenderer(RenderWidgetHost* renderer, bool monitor);
@@ -72,6 +101,26 @@ class ThumbnailGenerator : NotificationObserver {
void set_no_timeout(bool no_timeout) { no_timeout_ = no_timeout; }
#endif
+ // Calculates how "boring" a thumbnail is. The boring score is the
+ // 0,1 ranged percentage of pixels that are the most common
+ // luma. Higher boring scores indicate that a higher percentage of a
+ // bitmap are all the same brightness.
+ static double CalculateBoringScore(SkBitmap* bitmap);
+
+ // Gets the clipped bitmap from |bitmap| per the aspect ratio of the
+ // desired width and the desired height. For instance, if the input
+ // bitmap is vertically long (ex. 400x900) and the desired size is
+ // square (ex. 100x100), the clipped bitmap will be the top half of the
+ // input bitmap (400x400).
+ static SkBitmap GetClippedBitmap(const SkBitmap& bitmap,
+ int desired_width,
+ int desired_height,
+ ClipResult* clip_result);
+
+ // Update the thumbnail of the given URL if necessary.
+ static void UpdateThumbnailIfNecessary(TabContents* tab_contents,
+ const GURL& url);
+
private:
// RenderWidgetHostPaintingObserver implementation.
virtual void WidgetWillDestroyBackingStore(RenderWidgetHost* widget,
diff --git a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
index 787873c..7c7f601 100644
--- a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
+++ b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc
@@ -11,6 +11,7 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"
#include "chrome/test/testing_profile.h"
+#include "gfx/canvas_skia.h"
#include "skia/ext/platform_canvas.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColorPriv.h"
@@ -178,3 +179,102 @@ TEST_F(ThumbnailGeneratorTest, QuickShow) {
}
#endif
+
+TEST(ThumbnailGeneratorSimpleTest, CalculateBoringScore_Empty) {
+ SkBitmap bitmap;
+ EXPECT_DOUBLE_EQ(1.0, ThumbnailGenerator::CalculateBoringScore(&bitmap));
+}
+
+TEST(ThumbnailGeneratorSimpleTest, CalculateBoringScore_SingleColor) {
+ const SkColor kBlack = SkColorSetRGB(0, 0, 0);
+ const gfx::Size kSize(20, 10);
+ gfx::CanvasSkia canvas(kSize.width(), kSize.height(), true);
+ // Fill all pixesl in black.
+ canvas.FillRectInt(kBlack, 0, 0, kSize.width(), kSize.height());
+
+ SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+ // The thumbnail should deserve the highest boring score.
+ EXPECT_DOUBLE_EQ(1.0, ThumbnailGenerator::CalculateBoringScore(&bitmap));
+}
+
+TEST(ThumbnailGeneratorSimpleTest, CalculateBoringScore_TwoColors) {
+ const SkColor kBlack = SkColorSetRGB(0, 0, 0);
+ const SkColor kWhite = SkColorSetRGB(0xFF, 0xFF, 0xFF);
+ const gfx::Size kSize(20, 10);
+
+ gfx::CanvasSkia canvas(kSize.width(), kSize.height(), true);
+ // Fill all pixesl in black.
+ canvas.FillRectInt(kBlack, 0, 0, kSize.width(), kSize.height());
+ // Fill the left half pixels in white.
+ canvas.FillRectInt(kWhite, 0, 0, kSize.width() / 2, kSize.height());
+
+ SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+ ASSERT_EQ(kSize.width(), bitmap.width());
+ ASSERT_EQ(kSize.height(), bitmap.height());
+ // The thumbnail should be less boring because two colors are used.
+ EXPECT_DOUBLE_EQ(0.5, ThumbnailGenerator::CalculateBoringScore(&bitmap));
+}
+
+TEST(ThumbnailGeneratorSimpleTest, GetClippedBitmap_TallerThanWide) {
+ // The input bitmap is vertically long.
+ gfx::CanvasSkia canvas(40, 90, true);
+ const SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+
+ // The desired size is square.
+ ThumbnailGenerator::ClipResult clip_result = ThumbnailGenerator::kNotClipped;
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
+ bitmap, 10, 10, &clip_result);
+ // The clipped bitmap should be square.
+ EXPECT_EQ(40, clipped_bitmap.width());
+ EXPECT_EQ(40, clipped_bitmap.height());
+ // The input was taller than wide.
+ EXPECT_EQ(ThumbnailGenerator::kTallerThanWide, clip_result);
+}
+
+TEST(ThumbnailGeneratorSimpleTest, GetClippedBitmap_WiderThanTall) {
+ // The input bitmap is horizontally long.
+ gfx::CanvasSkia canvas(90, 40, true);
+ const SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+
+ // The desired size is square.
+ ThumbnailGenerator::ClipResult clip_result = ThumbnailGenerator::kNotClipped;
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
+ bitmap, 10, 10, &clip_result);
+ // The clipped bitmap should be square.
+ EXPECT_EQ(40, clipped_bitmap.width());
+ EXPECT_EQ(40, clipped_bitmap.height());
+ // The input was wider than tall.
+ EXPECT_EQ(ThumbnailGenerator::kWiderThanTall, clip_result);
+}
+
+TEST(ThumbnailGeneratorSimpleTest, GetClippedBitmap_NotClipped) {
+ // The input bitmap is square.
+ gfx::CanvasSkia canvas(40, 40, true);
+ const SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+
+ // The desired size is square.
+ ThumbnailGenerator::ClipResult clip_result = ThumbnailGenerator::kNotClipped;
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
+ bitmap, 10, 10, &clip_result);
+ // The clipped bitmap should be square.
+ EXPECT_EQ(40, clipped_bitmap.width());
+ EXPECT_EQ(40, clipped_bitmap.height());
+ // There was no need to clip.
+ EXPECT_EQ(ThumbnailGenerator::kNotClipped, clip_result);
+}
+
+TEST(ThumbnailGeneratorSimpleTest, GetClippedBitmap_NonSquareOutput) {
+ // The input bitmap is square.
+ gfx::CanvasSkia canvas(40, 40, true);
+ const SkBitmap bitmap = canvas.getTopPlatformDevice().accessBitmap(false);
+
+ // The desired size is horizontally long.
+ ThumbnailGenerator::ClipResult clip_result = ThumbnailGenerator::kNotClipped;
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap(
+ bitmap, 20, 10, &clip_result);
+ // The clipped bitmap should have the same aspect ratio of the desired size.
+ EXPECT_EQ(40, clipped_bitmap.width());
+ EXPECT_EQ(20, clipped_bitmap.height());
+ // The input was taller than wide.
+ EXPECT_EQ(ThumbnailGenerator::kTallerThanWide, clip_result);
+}
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 1a86bcd..f781baf 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -497,6 +497,11 @@ const char kEnableFastback[] = "enable-fastback";
// testing, for example page cycler and layout tests. See bug 1157243.
const char kEnableFileCookies[] = "enable-file-cookies";
+// Enable the in-browser thumbnailing, which is more efficient than the
+// in-renderer thumbnailing, as we can use more information to determine
+// if we need to update thumbnails.
+const char kEnableInBrowserThumbnailing[] = "enable-in-browser-thumbnailing";
+
// Enable IPv6 support, even if probes suggest that it may not be fully
// supported. Some probes may require internet connections, and this flag will
// allow support independent of application testing.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index d23b724..aa9c42d 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -149,6 +149,7 @@ extern const char kEnableExtensionTimelineApi[];
extern const char kEnableFastback[];
extern const char kEnableFileCookies[];
extern const char kEnableGPUPlugin[];
+extern const char kEnableInBrowserThumbnailing[];
extern const char kEnableIPv6[];
extern const char kEnableLogging[];
extern const char kEnableMemoryInfo[];
diff --git a/chrome/common/render_messages_params.cc b/chrome/common/render_messages_params.cc
index 91d7ba7..c93423f 100644
--- a/chrome/common/render_messages_params.cc
+++ b/chrome/common/render_messages_params.cc
@@ -790,6 +790,7 @@ void ParamTraits<ViewHostMsg_UpdateRect_Params>::Write(
WriteParam(m, p.dx);
WriteParam(m, p.dy);
WriteParam(m, p.scroll_rect);
+ WriteParam(m, p.scroll_offset);
WriteParam(m, p.copy_rects);
WriteParam(m, p.view_size);
WriteParam(m, p.resizer_rect);
@@ -805,6 +806,7 @@ bool ParamTraits<ViewHostMsg_UpdateRect_Params>::Read(
ReadParam(m, iter, &p->dx) &&
ReadParam(m, iter, &p->dy) &&
ReadParam(m, iter, &p->scroll_rect) &&
+ ReadParam(m, iter, &p->scroll_offset) &&
ReadParam(m, iter, &p->copy_rects) &&
ReadParam(m, iter, &p->view_size) &&
ReadParam(m, iter, &p->resizer_rect) &&
diff --git a/chrome/common/render_messages_params.h b/chrome/common/render_messages_params.h
index 20d8470..11fe638 100644
--- a/chrome/common/render_messages_params.h
+++ b/chrome/common/render_messages_params.h
@@ -344,6 +344,9 @@ struct ViewHostMsg_UpdateRect_Params {
// The rectangular region to scroll.
gfx::Rect scroll_rect;
+ // The scroll offset of the render view.
+ gfx::Size scroll_offset;
+
// The regions of the bitmap (in view coords) that contain updated pixels.
// In the case of scrolling, this includes the scroll damage rect.
std::vector<gfx::Rect> copy_rects;
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 151d927..c7df8bf 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -1227,7 +1227,13 @@ void RenderView::CapturePageInfo(int load_id, bool preliminary_capture) {
TranslateHelper::IsPageTranslatable(&document)));
}
- OnCaptureThumbnail();
+ // Generate the thumbnail here if the in-browser thumbnailing isn't
+ // enabled. TODO(satorux): Remove this and related code once
+ // crbug.com/65936 is complete.
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableInBrowserThumbnailing)) {
+ OnCaptureThumbnail();
+ }
if (phishing_delegate_.get())
phishing_delegate_->FinishedLoad(&contents);
@@ -5175,6 +5181,11 @@ webkit::ppapi::PluginInstance* RenderView::GetBitmapForOptimizedPluginPaint(
paint_bounds, dib, location, clip);
}
+gfx::Size RenderView::GetScrollOffset() {
+ WebKit::WebSize scroll_offset = webview()->mainFrame()->scrollOffset();
+ return gfx::Size(scroll_offset.width, scroll_offset.height);
+}
+
void RenderView::OnClearFocusedNode() {
if (webview())
webview()->clearFocusedNode();
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 2f4729a..fa357c4 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -665,6 +665,7 @@ class RenderView : public RenderWidget,
TransportDIB** dib,
gfx::Rect* location,
gfx::Rect* clip);
+ virtual gfx::Size GetScrollOffset();
virtual void DidHandleKeyEvent();
virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event);
virtual void OnSetFocus(bool enable);
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
index 7fb9e4a..ba01020 100644
--- a/chrome/renderer/render_widget.cc
+++ b/chrome/renderer/render_widget.cc
@@ -585,6 +585,7 @@ void RenderWidget::DoDeferredUpdate() {
params.resizer_rect = resizer_rect_;
params.plugin_window_moves.swap(plugin_window_moves_);
params.flags = next_paint_flags_;
+ params.scroll_offset = GetScrollOffset();
update_reply_pending_ = true;
Send(new ViewHostMsg_UpdateRect(routing_id_, params));
@@ -920,6 +921,11 @@ webkit::ppapi::PluginInstance* RenderWidget::GetBitmapForOptimizedPluginPaint(
return NULL;
}
+gfx::Size RenderWidget::GetScrollOffset() {
+ // Bare RenderWidgets don't support scroll offset.
+ return gfx::Size(0, 0);
+}
+
void RenderWidget::SetHidden(bool hidden) {
if (is_hidden_ == hidden)
return;
diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h
index 33d0205..71a065e 100644
--- a/chrome/renderer/render_widget.h
+++ b/chrome/renderer/render_widget.h
@@ -219,6 +219,10 @@ class RenderWidget : public IPC::Channel::Listener,
gfx::Rect* location,
gfx::Rect* clip);
+ // Gets the scroll offset of this widget, if this widget has a notion of
+ // scroll offset.
+ virtual gfx::Size GetScrollOffset();
+
// Sets the "hidden" state of this widget. All accesses to is_hidden_ should
// use this method so that we can properly inform the RenderThread of our
// state.