| Index: chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| diff --git a/chrome/browser/thumbnails/thumbnail_tab_helper.cc b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| index a5de4ee96d333b457f869314119c46252b4aef3a..93f6f42dc8a3eb3d2a5250cbf6ce5ba0e8c617e7 100644
|
| --- a/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| +++ b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| @@ -53,73 +53,10 @@ using thumbnails::ClipResult;
|
| using thumbnails::ThumbnailingContext;
|
| using thumbnails::ThumbnailingAlgorithm;
|
|
|
| -namespace {
|
| -
|
| -// Feed the constructed thumbnail to the thumbnail service.
|
| -void UpdateThumbnail(const ThumbnailingContext& context,
|
| - const SkBitmap& thumbnail) {
|
| - gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail);
|
| - context.service->SetPageThumbnail(context, image);
|
| - DVLOG(1) << "Thumbnail taken for " << context.url << ": "
|
| - << context.score.ToString();
|
| -}
|
| -
|
| -void ProcessCapturedBitmap(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);
|
| -
|
| - algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
|
| -}
|
| -
|
| -void AsyncProcessThumbnail(content::WebContents* web_contents,
|
| - scoped_refptr<ThumbnailingContext> context,
|
| - scoped_refptr<ThumbnailingAlgorithm> algorithm) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - RenderWidgetHost* render_widget_host =
|
| - web_contents->GetRenderViewHost()->GetWidget();
|
| - 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.
|
| - int scrollbar_size = gfx::scrollbar_size();
|
| - gfx::Size copy_size;
|
| - copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
|
| -
|
| - 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,
|
| - ©_rect,
|
| - &context->requested_copy_size);
|
| - render_widget_host->CopyFromBackingStore(
|
| - copy_rect,
|
| - context->requested_copy_size,
|
| - base::Bind(&ProcessCapturedBitmap, context, algorithm),
|
| - kN32_SkColorType);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
|
| : content::WebContentsObserver(contents),
|
| - load_interrupted_(false) {
|
| + load_interrupted_(false),
|
| + weak_factory_(this) {
|
| // Even though we deal in RenderWidgetHosts, we only care about its
|
| // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
|
| // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
|
| @@ -172,20 +109,29 @@ void ThumbnailTabHelper::NavigationStopped() {
|
| load_interrupted_ = true;
|
| }
|
|
|
| -void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
|
| - WebContents* web_contents) {
|
| +void ThumbnailTabHelper::UpdateThumbnailIfNecessary() {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + // Ignore thumbnail update requests if one is already in progress. This can
|
| + // happen at the end of thumbnail generation when
|
| + // CleanUpFromThumbnailGeneration() calls DecrementCapturerCount(), triggering
|
| + // a call to content::WebContentsImpl::WasHidden() which eventually calls
|
| + // ThumbnailTabHelper::UpdateThumbnailIfNecessary().
|
| + if (thumbnailing_context_) {
|
| + return;
|
| + }
|
| +
|
| // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
|
| // which would be unwise to attempt <http://crbug.com/130097>. If the
|
| // WebContents is in the middle of destruction, do not risk it.
|
| - if (!web_contents || web_contents->IsBeingDestroyed())
|
| + if (!web_contents() || web_contents()->IsBeingDestroyed())
|
| return;
|
| // Skip if a pending entry exists. WidgetHidden can be called while navigating
|
| // pages and this is not a time when thumbnails should be generated.
|
| - if (web_contents->GetController().GetPendingEntry())
|
| + if (web_contents()->GetController().GetPendingEntry())
|
| return;
|
| - const GURL& url = web_contents->GetURL();
|
| + const GURL& url = web_contents()->GetURL();
|
| Profile* profile =
|
| - Profile::FromBrowserContext(web_contents->GetBrowserContext());
|
| + Profile::FromBrowserContext(web_contents()->GetBrowserContext());
|
|
|
| scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
|
| ThumbnailServiceFactory::GetForProfile(profile);
|
| @@ -196,12 +142,102 @@ void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
|
| return;
|
| }
|
|
|
| + // Prevent the web contents from disappearing before the async thumbnail
|
| + // generation code executes. See https://crbug.com/530707 .
|
| + web_contents()->IncrementCapturerCount(gfx::Size());
|
| +
|
| + AsyncProcessThumbnail(thumbnail_service);
|
| +}
|
| +
|
| +void ThumbnailTabHelper::AsyncProcessThumbnail(
|
| + scoped_refptr<thumbnails::ThumbnailService> thumbnail_service) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + RenderWidgetHost* render_widget_host =
|
| + web_contents()->GetRenderViewHost()->GetWidget();
|
| + content::RenderWidgetHostView* view = render_widget_host->GetView();
|
| + if (!view || !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.
|
| + int scrollbar_size = gfx::scrollbar_size();
|
| + gfx::Size copy_size;
|
| + copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
|
| +
|
| + if (copy_rect.IsEmpty()) {
|
| + return;
|
| + }
|
| +
|
| scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
|
| thumbnail_service->GetThumbnailingAlgorithm());
|
|
|
| - scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
|
| - web_contents, thumbnail_service.get(), load_interrupted_));
|
| - AsyncProcessThumbnail(web_contents, context, algorithm);
|
| + thumbnailing_context_ = new ThumbnailingContext(web_contents(),
|
| + thumbnail_service.get(),
|
| + load_interrupted_);
|
| +
|
| + ui::ScaleFactor scale_factor =
|
| + ui::GetSupportedScaleFactor(
|
| + ui::GetScaleFactorForNativeView(view->GetNativeView()));
|
| + thumbnailing_context_->clip_result = algorithm->GetCanvasCopyInfo(
|
| + copy_rect.size(),
|
| + scale_factor,
|
| + ©_rect,
|
| + &thumbnailing_context_->requested_copy_size);
|
| + render_widget_host->CopyFromBackingStore(
|
| + copy_rect,
|
| + thumbnailing_context_->requested_copy_size,
|
| + base::Bind(&ThumbnailTabHelper::ProcessCapturedBitmap,
|
| + weak_factory_.GetWeakPtr(),
|
| + algorithm),
|
| + kN32_SkColorType);
|
| +}
|
| +
|
| +void ThumbnailTabHelper::ProcessCapturedBitmap(
|
| + scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm,
|
| + const SkBitmap& bitmap,
|
| + content::ReadbackResponse response) {
|
| + if (response == content::READBACK_SUCCESS) {
|
| + // On success, we must be on the UI thread.
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + algorithm->ProcessBitmap(thumbnailing_context_,
|
| + base::Bind(&ThumbnailTabHelper::UpdateThumbnail,
|
| + weak_factory_.GetWeakPtr()),
|
| + bitmap);
|
| + } else {
|
| + // On failure because of shutdown we are not on the UI thread, so ensure
|
| + // that cleanup happens on that thread.
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(&ThumbnailTabHelper::CleanUpFromThumbnailGeneration,
|
| + weak_factory_.GetWeakPtr()));
|
| + }
|
| +}
|
| +
|
| +void ThumbnailTabHelper::CleanUpFromThumbnailGeneration() {
|
| + if (web_contents()) {
|
| + // Balance the call to IncrementCapturerCount() made in
|
| + // UpdateThumbnailIfNecessary().
|
| + web_contents()->DecrementCapturerCount();
|
| + }
|
| +
|
| + // Make a note that thumbnail generation is complete.
|
| + thumbnailing_context_ = nullptr;
|
| +}
|
| +
|
| +void ThumbnailTabHelper::UpdateThumbnail(
|
| + const thumbnails::ThumbnailingContext& context,
|
| + const SkBitmap& thumbnail) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + // Feed the constructed thumbnail to the thumbnail service.
|
| + gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail);
|
| + context.service->SetPageThumbnail(context, image);
|
| + DVLOG(1) << "Thumbnail taken for " << context.url << ": "
|
| + << context.score.ToString();
|
| +
|
| + CleanUpFromThumbnailGeneration();
|
| }
|
|
|
| void ThumbnailTabHelper::RenderViewHostCreated(
|
| @@ -219,5 +255,5 @@ void ThumbnailTabHelper::RenderViewHostCreated(
|
| }
|
|
|
| void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) {
|
| - UpdateThumbnailIfNecessary(web_contents());
|
| + UpdateThumbnailIfNecessary();
|
| }
|
|
|