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
3 // found in the LICENSE file.
5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
7 #include "chrome/browser/browser_process.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/thumbnails/thumbnail_service.h"
10 #include "chrome/browser/thumbnails/thumbnail_service_factory.h"
11 #include "chrome/browser/thumbnails/thumbnailing_algorithm.h"
12 #include "chrome/browser/thumbnails/thumbnailing_context.h"
13 #include "content/public/browser/notification_details.h"
14 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/render_widget_host_view.h"
18 #include "ui/gfx/color_utils.h"
19 #include "ui/gfx/size_conversions.h"
20 #include "ui/gfx/screen.h"
21 #include "ui/gfx/scrollbar_size.h"
22 #include "ui/gfx/skbitmap_operations.h"
25 #include "base/win/windows_version.h"
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThumbnailTabHelper);
34 // This class provides a service for updating thumbnails to be used in
35 // "Most visited" section of the new tab page. The service can be started
36 // by StartThumbnailing(). The current algorithm of the service is as
39 // When a renderer is about to be hidden (this usually occurs when the
40 // current tab is closed or another tab is clicked), update the
41 // thumbnail for the tab rendered by the renderer, if needed. The
42 // heuristics to judge whether or not to update the thumbnail is
43 // implemented in ShouldUpdateThumbnail().
45 using content::RenderViewHost;
46 using content::RenderWidgetHost;
47 using content::WebContents;
49 using thumbnails::ClipResult;
50 using thumbnails::ThumbnailingContext;
51 using thumbnails::ThumbnailingAlgorithm;
55 // Feed the constructed thumbnail to the thumbnail service.
56 void UpdateThumbnail(const ThumbnailingContext& context,
57 const SkBitmap& thumbnail) {
58 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail);
59 context.service->SetPageThumbnail(context, image);
60 VLOG(1) << "Thumbnail taken for " << context.url << ": "
61 << context.score.ToString();
64 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context,
65 scoped_refptr<ThumbnailingAlgorithm> algorithm,
67 const SkBitmap& bitmap) {
71 // On success, we must be on the UI thread (on failure because of shutdown we
72 // are not on the UI thread).
73 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
75 algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
78 void GotSnapshotFromRenderer(base::Callback<void(const SkBitmap&)> callback,
80 const SkBitmap& bitmap) {
85 void AsyncProcessThumbnail(content::WebContents* web_contents,
86 scoped_refptr<ThumbnailingContext> context,
87 scoped_refptr<ThumbnailingAlgorithm> algorithm) {
88 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
89 RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost();
90 content::RenderWidgetHostView* view = render_widget_host->GetView();
93 if (!view->IsSurfaceAvailableForCopy()) {
94 // On Windows XP and possibly due to driver issues, neither the backing
95 // store nor the compositing surface is available in the browser when
96 // accelerated compositing is active, so ask the renderer to send a snapshot
97 // for creating the thumbnail.
98 render_widget_host->GetSnapshotFromRenderer(
100 base::Bind(GotSnapshotFromRenderer, base::Bind(
101 &ThumbnailingAlgorithm::ProcessBitmap,
102 algorithm, context, base::Bind(&UpdateThumbnail))));
106 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
107 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
109 int scrollbar_size = gfx::scrollbar_size();
111 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
113 if (copy_rect.IsEmpty())
116 context->clip_result = algorithm->GetCanvasCopyInfo(
118 ui::GetScaleFactorForNativeView(view->GetNativeView()),
120 &context->requested_copy_size);
122 render_widget_host->CopyFromBackingStore(
124 context->requested_copy_size,
125 base::Bind(&ProcessCapturedBitmap, context, algorithm));
130 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
131 : content::WebContentsObserver(contents),
133 load_interrupted_(false) {
134 // Even though we deal in RenderWidgetHosts, we only care about its
135 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
136 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
137 // aren't views like select popups.
139 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
140 content::Source<WebContents>(contents));
143 ThumbnailTabHelper::~ThumbnailTabHelper() {
146 void ThumbnailTabHelper::Observe(int type,
147 const content::NotificationSource& source,
148 const content::NotificationDetails& details) {
150 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED:
151 RenderViewHostCreated(content::Details<RenderViewHost>(details).ptr());
154 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED:
155 if (!*content::Details<bool>(details).ptr())
156 WidgetHidden(content::Source<RenderWidgetHost>(source).ptr());
160 NOTREACHED() << "Unexpected notification type: " << type;
164 void ThumbnailTabHelper::RenderViewDeleted(
165 content::RenderViewHost* render_view_host) {
166 bool registered = registrar_.IsRegistered(
168 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
169 content::Source<RenderWidgetHost>(render_view_host));
173 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
174 content::Source<RenderWidgetHost>(render_view_host));
178 void ThumbnailTabHelper::DidStartLoading(
179 content::RenderViewHost* render_view_host) {
180 load_interrupted_ = false;
183 void ThumbnailTabHelper::NavigationStopped() {
184 // This function gets called when the page loading is interrupted by the
186 load_interrupted_ = true;
189 void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
190 WebContents* web_contents) {
191 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
192 // which would be unwise to attempt <http://crbug.com/130097>. If the
193 // WebContents is in the middle of destruction, do not risk it.
194 if (!web_contents || web_contents->IsBeingDestroyed())
196 // Skip if a pending entry exists. WidgetHidden can be called while navigating
197 // pages and this is not a time when thumbnails should be generated.
198 if (web_contents->GetController().GetPendingEntry())
200 const GURL& url = web_contents->GetURL();
202 Profile::FromBrowserContext(web_contents->GetBrowserContext());
204 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
205 ThumbnailServiceFactory::GetForProfile(profile);
207 // Skip if we don't need to update the thumbnail.
208 if (thumbnail_service.get() == NULL ||
209 !thumbnail_service->ShouldAcquirePageThumbnail(url)) {
213 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
214 thumbnail_service->GetThumbnailingAlgorithm());
216 scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
217 web_contents, thumbnail_service.get(), load_interrupted_));
218 AsyncProcessThumbnail(web_contents, context, algorithm);
221 void ThumbnailTabHelper::RenderViewHostCreated(
222 content::RenderViewHost* renderer) {
223 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new
224 // RenderView, not RenderViewHost, and there is no good way to get
225 // notifications of RenderViewHosts. So just be tolerant of re-registrations.
226 bool registered = registrar_.IsRegistered(
228 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
229 content::Source<RenderWidgetHost>(renderer));
233 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
234 content::Source<RenderWidgetHost>(renderer));
238 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) {
241 UpdateThumbnailIfNecessary(web_contents());