| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" | 5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | |
| 8 #include "chrome/browser/browser_process.h" | 7 #include "chrome/browser/browser_process.h" |
| 9 #include "chrome/browser/profiles/profile.h" | 8 #include "chrome/browser/profiles/profile.h" |
| 10 #include "chrome/browser/thumbnails/render_widget_snapshot_taker.h" | 9 #include "chrome/browser/thumbnails/render_widget_snapshot_taker.h" |
| 11 #include "chrome/browser/thumbnails/thumbnail_service.h" | 10 #include "chrome/browser/thumbnails/thumbnail_service.h" |
| 12 #include "chrome/browser/thumbnails/thumbnail_service_factory.h" | 11 #include "chrome/browser/thumbnails/thumbnail_service_factory.h" |
| 12 #include "chrome/browser/thumbnails/thumbnailing_algorithm.h" |
| 13 #include "chrome/browser/thumbnails/thumbnailing_context.h" |
| 13 #include "content/public/browser/notification_details.h" | 14 #include "content/public/browser/notification_details.h" |
| 14 #include "content/public/browser/notification_source.h" | 15 #include "content/public/browser/notification_source.h" |
| 15 #include "content/public/browser/notification_types.h" | 16 #include "content/public/browser/notification_types.h" |
| 16 #include "content/public/browser/render_view_host.h" | 17 #include "content/public/browser/render_view_host.h" |
| 17 #include "content/public/browser/render_widget_host_view.h" | 18 #include "content/public/browser/render_widget_host_view.h" |
| 18 #include "skia/ext/platform_canvas.h" | 19 #include "skia/ext/platform_canvas.h" |
| 19 #include "ui/gfx/color_utils.h" | 20 #include "ui/gfx/color_utils.h" |
| 20 #include "ui/gfx/size_conversions.h" | 21 #include "ui/gfx/size_conversions.h" |
| 21 #include "ui/gfx/screen.h" | 22 #include "ui/gfx/screen.h" |
| 22 #include "ui/gfx/scrollbar_size.h" | 23 #include "ui/gfx/scrollbar_size.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 38 // When a renderer is about to be hidden (this usually occurs when the | 39 // When a renderer is about to be hidden (this usually occurs when the |
| 39 // current tab is closed or another tab is clicked), update the | 40 // current tab is closed or another tab is clicked), update the |
| 40 // thumbnail for the tab rendered by the renderer, if needed. The | 41 // thumbnail for the tab rendered by the renderer, if needed. The |
| 41 // heuristics to judge whether or not to update the thumbnail is | 42 // heuristics to judge whether or not to update the thumbnail is |
| 42 // implemented in ShouldUpdateThumbnail(). | 43 // implemented in ShouldUpdateThumbnail(). |
| 43 | 44 |
| 44 using content::RenderViewHost; | 45 using content::RenderViewHost; |
| 45 using content::RenderWidgetHost; | 46 using content::RenderWidgetHost; |
| 46 using content::WebContents; | 47 using content::WebContents; |
| 47 | 48 |
| 49 using thumbnails::ClipResult; |
| 50 using thumbnails::ThumbnailingContext; |
| 51 using thumbnails::ThumbnailingAlgorithm; |
| 52 |
| 48 namespace { | 53 namespace { |
| 49 | 54 |
| 50 // The thumbnail size in DIP. | 55 // Feed the constructed thumbnail to the thumbnail service. |
| 51 static const int kThumbnailWidth = 212; | 56 void UpdateThumbnail(const ThumbnailingContext& context, |
| 52 static const int kThumbnailHeight = 132; | 57 const SkBitmap& thumbnail) { |
| 53 | 58 gfx::Image image(thumbnail); |
| 54 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS"; | 59 context.service->SetPageThumbnail(context, image); |
| 55 | 60 VLOG(1) << "Thumbnail taken for " << context.url << ": " |
| 56 // Returns the size used by RenderWidgetHost::CopyFromBackingStore. | 61 << context.score.ToString(); |
| 57 // | |
| 58 // The size is calculated in such a way that the copied size in pixel becomes | |
| 59 // equal to (f * kThumbnailWidth, f * kThumbnailHeight), where f is the scale | |
| 60 // of ui::SCALE_FACTOR_200P. Since RenderWidgetHost::CopyFromBackingStore takes | |
| 61 // the size in DIP, we need to adjust the size based on |view|'s device scale | |
| 62 // factor in order to copy the pixels with the size above. | |
| 63 // | |
| 64 // The copied size was chosen for the following reasons. | |
| 65 // | |
| 66 // 1. When the scale factor of the primary monitor is ui::SCALE_FACTOR_200P, the | |
| 67 // generated thumbnail size is (f * kThumbnailWidth, f * kThumbnailHeight). | |
| 68 // In order to avoid degrading the image quality by magnification, the size | |
| 69 // of the copied pixels should be equal to or larger than this thumbnail size. | |
| 70 // | |
| 71 // 2. RenderWidgetHost::CopyFromBackingStore can be costly especially when | |
| 72 // it is necessary to read back the web contents image data from GPU. As the | |
| 73 // cost is roughly propotional to the number of the copied pixels, the size of | |
| 74 // the copied pixels should be as small as possible. | |
| 75 // | |
| 76 // When the scale factor of the primary monitor is ui::SCALE_FACTOR_100P, | |
| 77 // we still copy the pixels with the same size as ui::SCALE_FACTOR_200P because | |
| 78 // the resampling method used in RenderWidgetHost::CopyFromBackingStore is not | |
| 79 // good enough for the resampled image to be used directly for the thumbnail | |
| 80 // (http://crbug.com/141235). We assume this is not an issue in case of | |
| 81 // ui::SCALE_FACTOR_200P because the high resolution thumbnail on high density | |
| 82 // display alleviates the aliasing. | |
| 83 // TODO(mazda): Copy the pixels with the smaller size in the case of | |
| 84 // ui::SCALE_FACTOR_100P once the resampling method has been improved. | |
| 85 gfx::Size GetCopySizeForThumbnail(content::RenderWidgetHostView* view) { | |
| 86 gfx::Size copy_size(kThumbnailWidth, kThumbnailHeight); | |
| 87 ui::ScaleFactor scale_factor = | |
| 88 ui::GetScaleFactorForNativeView(view->GetNativeView()); | |
| 89 switch (scale_factor) { | |
| 90 case ui::SCALE_FACTOR_100P: | |
| 91 copy_size = gfx::ToFlooredSize(gfx::ScaleSize( | |
| 92 copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P))); | |
| 93 break; | |
| 94 case ui::SCALE_FACTOR_200P: | |
| 95 // Use the size as-is. | |
| 96 break; | |
| 97 default: | |
| 98 DLOG(WARNING) << "Unsupported scale factor. Use the same copy size as " | |
| 99 << "ui::SCALE_FACTOR_100P"; | |
| 100 copy_size = gfx::ToFlooredSize(gfx::ScaleSize( | |
| 101 copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P))); | |
| 102 break; | |
| 103 } | |
| 104 return copy_size; | |
| 105 } | 62 } |
| 106 | 63 |
| 107 // Returns the size of the thumbnail stored in the database in pixel. | 64 void ProcessCanvas(ThumbnailingContext* context, |
| 108 gfx::Size GetThumbnailSizeInPixel() { | 65 ThumbnailingAlgorithm* algorithm, |
| 109 gfx::Size thumbnail_size(kThumbnailWidth, kThumbnailHeight); | 66 skia::PlatformBitmap* temp_bitmap, |
| 110 // Determine the resolution of the thumbnail based on the maximum scale | 67 bool succeeded) { |
| 111 // factor. | 68 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 112 // TODO(mazda|oshima): Update thumbnail when the max scale factor changes. | 69 if (!succeeded) |
| 113 // crbug.com/159157. | 70 return; |
| 114 float max_scale_factor = | 71 |
| 115 ui::GetScaleFactorScale(ui::GetMaxScaleFactor()); | 72 SkBitmap bitmap = temp_bitmap->GetBitmap(); |
| 116 return gfx::ToFlooredSize(gfx::ScaleSize(thumbnail_size, max_scale_factor)); | 73 algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap); |
| 117 } | 74 } |
| 118 | 75 |
| 119 // Returns the clipping rectangle that is used for creating a thumbnail with | 76 void AsyncProcessThumbnail(content::WebContents* web_contents, |
| 120 // the size of |desired_size| from the bitmap with the size of |source_size|. | 77 scoped_refptr<ThumbnailingContext> context, |
| 121 // The type of clipping that needs to be done is assigned to |clip_result|. | 78 scoped_refptr<ThumbnailingAlgorithm> algorithm) { |
| 122 gfx::Rect GetClippingRect(const gfx::Size& source_size, | 79 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 123 const gfx::Size& desired_size, | 80 RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost(); |
| 124 ThumbnailTabHelper::ClipResult* clip_result) { | 81 content::RenderWidgetHostView* view = render_widget_host->GetView(); |
| 125 DCHECK(clip_result); | 82 if (!view) |
| 126 | 83 return; |
| 127 float desired_aspect = | 84 if (!view->IsSurfaceAvailableForCopy()) { |
| 128 static_cast<float>(desired_size.width()) / desired_size.height(); | 85 #if defined(OS_WIN) |
| 129 | 86 // On Windows XP, neither the backing store nor the compositing surface is |
| 130 // Get the clipping rect so that we can preserve the aspect ratio while | 87 // available in the browser when accelerated compositing is active, so ask |
| 131 // filling the destination. | 88 // the renderer to send a snapshot for creating the thumbnail. |
| 132 gfx::Rect clipping_rect; | 89 if (base::win::GetVersion() < base::win::VERSION_VISTA) { |
| 133 if (source_size.width() < desired_size.width() || | 90 gfx::Size view_size = |
| 134 source_size.height() < desired_size.height()) { | 91 render_widget_host->GetView()->GetViewBounds().size(); |
| 135 // Source image is smaller: we clip the part of source image within the | 92 g_browser_process->GetRenderWidgetSnapshotTaker()->AskForSnapshot( |
| 136 // dest rect, and then stretch it to fill the dest rect. We don't respect | 93 render_widget_host, |
| 137 // the aspect ratio in this case. | 94 base::Bind(&ThumbnailingAlgorithm::ProcessBitmap, |
| 138 clipping_rect = gfx::Rect(desired_size); | 95 algorithm, context, base::Bind(&UpdateThumbnail)), |
| 139 *clip_result = ThumbnailTabHelper::kSourceIsSmaller; | 96 view_size, |
| 140 } else { | 97 view_size); |
| 141 float src_aspect = | |
| 142 static_cast<float>(source_size.width()) / source_size.height(); | |
| 143 if (src_aspect > desired_aspect) { | |
| 144 // Wider than tall, clip horizontally: we center the smaller | |
| 145 // thumbnail in the wider screen. | |
| 146 int new_width = static_cast<int>(source_size.height() * desired_aspect); | |
| 147 int x_offset = (source_size.width() - new_width) / 2; | |
| 148 clipping_rect.SetRect(x_offset, 0, new_width, source_size.height()); | |
| 149 *clip_result = (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ? | |
| 150 ThumbnailTabHelper::kTooWiderThanTall : | |
| 151 ThumbnailTabHelper::kWiderThanTall; | |
| 152 } else if (src_aspect < desired_aspect) { | |
| 153 clipping_rect = | |
| 154 gfx::Rect(source_size.width(), source_size.width() / desired_aspect); | |
| 155 *clip_result = ThumbnailTabHelper::kTallerThanWide; | |
| 156 } else { | |
| 157 clipping_rect = gfx::Rect(source_size); | |
| 158 *clip_result = ThumbnailTabHelper::kNotClipped; | |
| 159 } | 98 } |
| 160 } | 99 #endif |
| 161 return clipping_rect; | 100 return; |
| 162 } | |
| 163 | |
| 164 // Creates a downsampled thumbnail from the given bitmap. | |
| 165 // store. The returned bitmap will be isNull if there was an error creating it. | |
| 166 SkBitmap CreateThumbnail( | |
| 167 const SkBitmap& bitmap, | |
| 168 const gfx::Size& desired_size, | |
| 169 ThumbnailTabHelper::ClipResult* clip_result) { | |
| 170 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); | |
| 171 | |
| 172 SkBitmap clipped_bitmap; | |
| 173 if (*clip_result == ThumbnailTabHelper::kUnprocessed) { | |
| 174 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | |
| 175 // thumbnails. | |
| 176 int scrollbar_size = gfx::scrollbar_size(); | |
| 177 SkIRect scrollbarless_rect = | |
| 178 { 0, 0, | |
| 179 std::max(1, bitmap.width() - scrollbar_size), | |
| 180 std::max(1, bitmap.height() - scrollbar_size) }; | |
| 181 SkBitmap bmp; | |
| 182 bitmap.extractSubset(&bmp, scrollbarless_rect); | |
| 183 | |
| 184 clipped_bitmap = ThumbnailTabHelper::GetClippedBitmap( | |
| 185 bmp, desired_size.width(), desired_size.height(), clip_result); | |
| 186 } else { | |
| 187 clipped_bitmap = bitmap; | |
| 188 } | 101 } |
| 189 | 102 |
| 190 // Need to resize it to the size we want, so downsample until it's | 103 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); |
| 191 // close, and let the caller make it the exact size if desired. | 104 // Clip the pixels that will commonly hold a scrollbar, which looks bad in |
| 192 SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize( | 105 // thumbnails. |
| 193 clipped_bitmap, desired_size.width(), desired_size.height()); | 106 int scrollbar_size = gfx::scrollbar_size(); |
| 194 #if !defined(USE_AURA) | 107 gfx::Size copy_size; |
| 195 // This is a bit subtle. SkBitmaps are refcounted, but the magic | 108 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); |
| 196 // ones in PlatformCanvas can't be assigned to SkBitmap with proper | |
| 197 // refcounting. If the bitmap doesn't change, then the downsampler | |
| 198 // will return the input bitmap, which will be the reference to the | |
| 199 // weird PlatformCanvas one insetad of a regular one. To get a | |
| 200 // regular refcounted bitmap, we need to copy it. | |
| 201 // | |
| 202 // On Aura, the PlatformCanvas is platform-independent and does not have | |
| 203 // any native platform resources that can't be refounted, so this issue does | |
| 204 // not occur. | |
| 205 // | |
| 206 // Note that GetClippedBitmap() does extractSubset() but it won't copy | |
| 207 // the pixels, hence we check result size == clipped_bitmap size here. | |
| 208 if (clipped_bitmap.width() == result.width() && | |
| 209 clipped_bitmap.height() == result.height()) | |
| 210 clipped_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config); | |
| 211 #endif | |
| 212 | 109 |
| 213 HISTOGRAM_TIMES(kThumbnailHistogramName, | 110 if (copy_rect.IsEmpty()) |
| 214 base::TimeTicks::Now() - begin_compute_thumbnail); | 111 return; |
| 215 return result; | 112 |
| 113 context->clip_result = algorithm->GetCanvasCopyInfo( |
| 114 copy_rect.size(), |
| 115 ui::GetScaleFactorForNativeView(view->GetNativeView()), |
| 116 ©_rect, |
| 117 ©_size); |
| 118 |
| 119 skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap; |
| 120 render_widget_host->CopyFromBackingStore( |
| 121 copy_rect, |
| 122 copy_size, |
| 123 base::Bind(&ProcessCanvas, context, algorithm, base::Owned(temp_bitmap)), |
| 124 temp_bitmap); |
| 216 } | 125 } |
| 217 | 126 |
| 218 } // namespace | 127 } // namespace |
| 219 | 128 |
| 220 ThumbnailTabHelper::ThumbnailingContext::ThumbnailingContext( | |
| 221 content::WebContents* web_contents, | |
| 222 bool load_interrupted) | |
| 223 : browser_context(web_contents->GetBrowserContext()), | |
| 224 url(web_contents->GetURL()), | |
| 225 clip_result(kUnprocessed) { | |
| 226 score.at_top = | |
| 227 (web_contents->GetRenderViewHost()->GetLastScrollOffset().y() == 0); | |
| 228 score.load_completed = !web_contents->IsLoading() && !load_interrupted; | |
| 229 } | |
| 230 | |
| 231 ThumbnailTabHelper::ThumbnailingContext::~ThumbnailingContext() { | |
| 232 } | |
| 233 | |
| 234 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) | 129 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) |
| 235 : content::WebContentsObserver(contents), | 130 : content::WebContentsObserver(contents), |
| 236 enabled_(true), | 131 enabled_(true), |
| 237 load_interrupted_(false) { | 132 load_interrupted_(false) { |
| 238 // Even though we deal in RenderWidgetHosts, we only care about its | 133 // Even though we deal in RenderWidgetHosts, we only care about its |
| 239 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails | 134 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails |
| 240 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that | 135 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that |
| 241 // aren't views like select popups. | 136 // aren't views like select popups. |
| 242 registrar_.Add(this, | 137 registrar_.Add(this, |
| 243 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, | 138 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 registrar_.Remove( | 198 registrar_.Remove( |
| 304 this, | 199 this, |
| 305 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 200 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
| 306 content::Source<RenderWidgetHost>(renderer)); | 201 content::Source<RenderWidgetHost>(renderer)); |
| 307 registrar_.Remove( | 202 registrar_.Remove( |
| 308 this, | 203 this, |
| 309 content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, | 204 content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, |
| 310 content::Source<RenderViewHost>(renderer)); | 205 content::Source<RenderViewHost>(renderer)); |
| 311 } | 206 } |
| 312 | 207 |
| 313 double ThumbnailTabHelper::CalculateBoringScore(const SkBitmap& bitmap) { | |
| 314 if (bitmap.isNull() || bitmap.empty()) | |
| 315 return 1.0; | |
| 316 int histogram[256] = {0}; | |
| 317 color_utils::BuildLumaHistogram(bitmap, histogram); | |
| 318 | |
| 319 int color_count = *std::max_element(histogram, histogram + 256); | |
| 320 int pixel_count = bitmap.width() * bitmap.height(); | |
| 321 return static_cast<double>(color_count) / pixel_count; | |
| 322 } | |
| 323 | |
| 324 SkBitmap ThumbnailTabHelper::GetClippedBitmap(const SkBitmap& bitmap, | |
| 325 int desired_width, | |
| 326 int desired_height, | |
| 327 ClipResult* clip_result) { | |
| 328 gfx::Rect clipping_rect = | |
| 329 GetClippingRect(gfx::Size(bitmap.width(), bitmap.height()), | |
| 330 gfx::Size(desired_width, desired_height), | |
| 331 clip_result); | |
| 332 SkIRect src_rect = { clipping_rect.x(), clipping_rect.y(), | |
| 333 clipping_rect.right(), clipping_rect.bottom() }; | |
| 334 SkBitmap clipped_bitmap; | |
| 335 bitmap.extractSubset(&clipped_bitmap, src_rect); | |
| 336 return clipped_bitmap; | |
| 337 } | |
| 338 | |
| 339 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( | 208 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( |
| 340 WebContents* web_contents) { | 209 WebContents* web_contents) { |
| 341 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot | 210 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot |
| 342 // which would be unwise to attempt <http://crbug.com/130097>. If the | 211 // which would be unwise to attempt <http://crbug.com/130097>. If the |
| 343 // WebContents is in the middle of destruction, do not risk it. | 212 // WebContents is in the middle of destruction, do not risk it. |
| 344 if (web_contents->IsBeingDestroyed()) | 213 if (web_contents->IsBeingDestroyed()) |
| 345 return; | 214 return; |
| 346 // Skip if a pending entry exists. WidgetHidden can be called while navigating | 215 // Skip if a pending entry exists. WidgetHidden can be called while navigating |
| 347 // pages and this is not a time when thumbnails should be generated. | 216 // pages and this is not a time when thumbnails should be generated. |
| 348 if (web_contents->GetController().GetPendingEntry()) | 217 if (web_contents->GetController().GetPendingEntry()) |
| 349 return; | 218 return; |
| 350 const GURL& url = web_contents->GetURL(); | 219 const GURL& url = web_contents->GetURL(); |
| 351 Profile* profile = | 220 Profile* profile = |
| 352 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 221 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 353 | 222 |
| 354 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = | 223 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = |
| 355 ThumbnailServiceFactory::GetForProfile(profile); | 224 ThumbnailServiceFactory::GetForProfile(profile); |
| 356 | 225 |
| 357 // Skip if we don't need to update the thumbnail. | 226 // Skip if we don't need to update the thumbnail. |
| 358 if (thumbnail_service == NULL || | 227 if (thumbnail_service == NULL || |
| 359 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { | 228 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { |
| 360 return; | 229 return; |
| 361 } | 230 } |
| 362 | 231 |
| 363 AsyncUpdateThumbnail(web_contents); | 232 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( |
| 364 } | 233 thumbnail_service->GetThumbnailingAlgorithm()); |
| 365 | |
| 366 void ThumbnailTabHelper::UpdateThumbnail( | |
| 367 ThumbnailingContext* context, | |
| 368 const SkBitmap& thumbnail) { | |
| 369 Profile* profile = | |
| 370 Profile::FromBrowserContext(context->browser_context); | |
| 371 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = | |
| 372 ThumbnailServiceFactory::GetForProfile(profile); | |
| 373 | |
| 374 if (!thumbnail_service) | |
| 375 return; | |
| 376 | |
| 377 context->score.boring_score = CalculateBoringScore(thumbnail); | |
| 378 context->score.good_clipping = | |
| 379 (context->clip_result == ThumbnailTabHelper::kWiderThanTall || | |
| 380 context->clip_result == ThumbnailTabHelper::kTallerThanWide || | |
| 381 context->clip_result == ThumbnailTabHelper::kNotClipped); | |
| 382 | |
| 383 gfx::Image image(thumbnail); | |
| 384 thumbnail_service->SetPageThumbnail(context->url, image, context->score); | |
| 385 VLOG(1) << "Thumbnail taken for " << context->url << ": " | |
| 386 << context->score.ToString(); | |
| 387 } | |
| 388 | |
| 389 void ThumbnailTabHelper::AsyncUpdateThumbnail( | |
| 390 WebContents* web_contents) { | |
| 391 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 392 RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost(); | |
| 393 content::RenderWidgetHostView* view = render_widget_host->GetView(); | |
| 394 if (!view) | |
| 395 return; | |
| 396 if (!view->IsSurfaceAvailableForCopy()) { | |
| 397 #if defined(OS_WIN) | |
| 398 // On Windows XP, neither the backing store nor the compositing surface is | |
| 399 // available in the browser when accelerated compositing is active, so ask | |
| 400 // the renderer to send a snapshot for creating the thumbnail. | |
| 401 if (base::win::GetVersion() < base::win::VERSION_VISTA) { | |
| 402 scoped_refptr<ThumbnailingContext> context( | |
| 403 new ThumbnailingContext(web_contents, load_interrupted_)); | |
| 404 gfx::Size view_size = | |
| 405 render_widget_host->GetView()->GetViewBounds().size(); | |
| 406 g_browser_process->GetRenderWidgetSnapshotTaker()->AskForSnapshot( | |
| 407 render_widget_host, | |
| 408 base::Bind(&ThumbnailTabHelper::UpdateThumbnailWithBitmap, | |
| 409 context), | |
| 410 view_size, | |
| 411 view_size); | |
| 412 } | |
| 413 #endif | |
| 414 return; | |
| 415 } | |
| 416 | |
| 417 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); | |
| 418 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | |
| 419 // thumbnails. | |
| 420 int scrollbar_size = gfx::scrollbar_size(); | |
| 421 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); | |
| 422 | 234 |
| 423 scoped_refptr<ThumbnailingContext> context( | 235 scoped_refptr<ThumbnailingContext> context( |
| 424 new ThumbnailingContext(web_contents, load_interrupted_)); | 236 new ThumbnailingContext(web_contents, |
| 425 copy_rect = GetClippingRect(copy_rect.size(), | 237 thumbnail_service, |
| 426 gfx::Size(kThumbnailWidth, kThumbnailHeight), | 238 load_interrupted_)); |
| 427 &context->clip_result); | 239 AsyncProcessThumbnail(web_contents, context, algorithm); |
| 428 | |
| 429 gfx::Size copy_size = GetCopySizeForThumbnail(view); | |
| 430 skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap; | |
| 431 render_widget_host->CopyFromBackingStore( | |
| 432 copy_rect, | |
| 433 copy_size, | |
| 434 base::Bind(&ThumbnailTabHelper::UpdateThumbnailWithCanvas, | |
| 435 context, | |
| 436 base::Owned(temp_bitmap)), | |
| 437 temp_bitmap); | |
| 438 } | |
| 439 | |
| 440 void ThumbnailTabHelper::UpdateThumbnailWithBitmap( | |
| 441 ThumbnailingContext* context, | |
| 442 const SkBitmap& bitmap) { | |
| 443 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 444 if (bitmap.isNull() || bitmap.empty()) | |
| 445 return; | |
| 446 | |
| 447 SkBitmap thumbnail = CreateThumbnail(bitmap, | |
| 448 GetThumbnailSizeInPixel(), | |
| 449 &context->clip_result); | |
| 450 | |
| 451 UpdateThumbnail(context, thumbnail); | |
| 452 } | |
| 453 | |
| 454 void ThumbnailTabHelper::UpdateThumbnailWithCanvas( | |
| 455 ThumbnailingContext* context, | |
| 456 skia::PlatformBitmap* temp_bitmap, | |
| 457 bool succeeded) { | |
| 458 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 459 if (!succeeded) | |
| 460 return; | |
| 461 | |
| 462 SkBitmap bitmap = temp_bitmap->GetBitmap(); | |
| 463 UpdateThumbnailWithBitmap(context, bitmap); | |
| 464 } | 240 } |
| 465 | 241 |
| 466 void ThumbnailTabHelper::DidStartLoading( | 242 void ThumbnailTabHelper::DidStartLoading( |
| 467 content::RenderViewHost* render_view_host) { | 243 content::RenderViewHost* render_view_host) { |
| 468 load_interrupted_ = false; | 244 load_interrupted_ = false; |
| 469 } | 245 } |
| 470 | 246 |
| 471 void ThumbnailTabHelper::StopNavigation() { | 247 void ThumbnailTabHelper::StopNavigation() { |
| 472 // This function gets called when the page loading is interrupted by the | 248 // This function gets called when the page loading is interrupted by the |
| 473 // stop button. | 249 // stop button. |
| 474 load_interrupted_ = true; | 250 load_interrupted_ = true; |
| 475 } | 251 } |
| OLD | NEW |