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/render_widget_snapshot_taker.h"
10 #include "chrome/browser/thumbnails/thumbnail_service.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"
14 #include "content/public/browser/notification_details.h"
15 #include "content/public/browser/notification_source.h"
16 #include "content/public/browser/notification_types.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "ui/gfx/color_utils.h"
20 #include "ui/gfx/size_conversions.h"
21 #include "ui/gfx/screen.h"
22 #include "ui/gfx/scrollbar_size.h"
23 #include "ui/gfx/skbitmap_operations.h"
26 #include "base/win/windows_version.h"
29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThumbnailTabHelper
);
35 // This class provides a service for updating thumbnails to be used in
36 // "Most visited" section of the new tab page. The service can be started
37 // by StartThumbnailing(). The current algorithm of the service is as
40 // When a renderer is about to be hidden (this usually occurs when the
41 // current tab is closed or another tab is clicked), update the
42 // thumbnail for the tab rendered by the renderer, if needed. The
43 // heuristics to judge whether or not to update the thumbnail is
44 // implemented in ShouldUpdateThumbnail().
46 using content::RenderViewHost
;
47 using content::RenderWidgetHost
;
48 using content::WebContents
;
50 using thumbnails::ClipResult
;
51 using thumbnails::ThumbnailingContext
;
52 using thumbnails::ThumbnailingAlgorithm
;
56 // Feed the constructed thumbnail to the thumbnail service.
57 void UpdateThumbnail(const ThumbnailingContext
& context
,
58 const SkBitmap
& thumbnail
) {
59 gfx::Image image
= gfx::Image::CreateFrom1xBitmap(thumbnail
);
60 context
.service
->SetPageThumbnail(context
, image
);
61 VLOG(1) << "Thumbnail taken for " << context
.url
<< ": "
62 << context
.score
.ToString();
65 void ProcessCapturedBitmap(scoped_refptr
<ThumbnailingContext
> context
,
66 scoped_refptr
<ThumbnailingAlgorithm
> algorithm
,
68 const SkBitmap
& bitmap
) {
72 // On success, we must be on the UI thread (on failure because of shutdown we
73 // are not on the UI thread).
74 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
76 algorithm
->ProcessBitmap(context
, base::Bind(&UpdateThumbnail
), bitmap
);
79 void GotSnapshotFromRenderer(base::Callback
<void(const SkBitmap
&)> callback
,
81 const SkBitmap
& bitmap
) {
86 void AsyncProcessThumbnail(content::WebContents
* web_contents
,
87 scoped_refptr
<ThumbnailingContext
> context
,
88 scoped_refptr
<ThumbnailingAlgorithm
> algorithm
) {
89 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
90 RenderWidgetHost
* render_widget_host
= web_contents
->GetRenderViewHost();
91 content::RenderWidgetHostView
* view
= render_widget_host
->GetView();
94 if (!view
->IsSurfaceAvailableForCopy()) {
95 // On Windows XP and possibly due to driver issues, neither the backing
96 // store nor the compositing surface is available in the browser when
97 // accelerated compositing is active, so ask the renderer to send a snapshot
98 // for creating the thumbnail.
99 render_widget_host
->GetSnapshotFromRenderer(
101 base::Bind(GotSnapshotFromRenderer
, base::Bind(
102 &ThumbnailingAlgorithm::ProcessBitmap
,
103 algorithm
, context
, base::Bind(&UpdateThumbnail
))));
107 gfx::Rect copy_rect
= gfx::Rect(view
->GetViewBounds().size());
108 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
110 int scrollbar_size
= gfx::scrollbar_size();
112 copy_rect
.Inset(0, 0, scrollbar_size
, scrollbar_size
);
114 if (copy_rect
.IsEmpty())
117 context
->clip_result
= algorithm
->GetCanvasCopyInfo(
119 ui::GetScaleFactorForNativeView(view
->GetNativeView()),
121 &context
->requested_copy_size
);
123 render_widget_host
->CopyFromBackingStore(
125 context
->requested_copy_size
,
126 base::Bind(&ProcessCapturedBitmap
, context
, algorithm
));
131 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents
* contents
)
132 : content::WebContentsObserver(contents
),
134 load_interrupted_(false) {
135 // Even though we deal in RenderWidgetHosts, we only care about its
136 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
137 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
138 // aren't views like select popups.
140 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
,
141 content::Source
<WebContents
>(contents
));
144 ThumbnailTabHelper::~ThumbnailTabHelper() {
147 void ThumbnailTabHelper::Observe(int type
,
148 const content::NotificationSource
& source
,
149 const content::NotificationDetails
& details
) {
151 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
:
152 RenderViewHostCreated(content::Details
<RenderViewHost
>(details
).ptr());
155 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
:
156 if (!*content::Details
<bool>(details
).ptr())
157 WidgetHidden(content::Source
<RenderWidgetHost
>(source
).ptr());
161 NOTREACHED() << "Unexpected notification type: " << type
;
165 void ThumbnailTabHelper::RenderViewDeleted(
166 content::RenderViewHost
* render_view_host
) {
167 g_browser_process
->GetRenderWidgetSnapshotTaker()->CancelSnapshot(
170 bool registered
= registrar_
.IsRegistered(
172 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
173 content::Source
<RenderWidgetHost
>(render_view_host
));
177 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
178 content::Source
<RenderWidgetHost
>(render_view_host
));
182 void ThumbnailTabHelper::DidStartLoading(
183 content::RenderViewHost
* render_view_host
) {
184 load_interrupted_
= false;
187 void ThumbnailTabHelper::NavigationStopped() {
188 // This function gets called when the page loading is interrupted by the
190 load_interrupted_
= true;
193 void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
194 WebContents
* web_contents
) {
195 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
196 // which would be unwise to attempt <http://crbug.com/130097>. If the
197 // WebContents is in the middle of destruction, do not risk it.
198 if (!web_contents
|| web_contents
->IsBeingDestroyed())
200 // Skip if a pending entry exists. WidgetHidden can be called while navigating
201 // pages and this is not a time when thumbnails should be generated.
202 if (web_contents
->GetController().GetPendingEntry())
204 const GURL
& url
= web_contents
->GetURL();
206 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
208 scoped_refptr
<thumbnails::ThumbnailService
> thumbnail_service
=
209 ThumbnailServiceFactory::GetForProfile(profile
);
211 // Skip if we don't need to update the thumbnail.
212 if (thumbnail_service
.get() == NULL
||
213 !thumbnail_service
->ShouldAcquirePageThumbnail(url
)) {
217 scoped_refptr
<thumbnails::ThumbnailingAlgorithm
> algorithm(
218 thumbnail_service
->GetThumbnailingAlgorithm());
220 scoped_refptr
<ThumbnailingContext
> context(new ThumbnailingContext(
221 web_contents
, thumbnail_service
.get(), load_interrupted_
));
222 AsyncProcessThumbnail(web_contents
, context
, algorithm
);
225 void ThumbnailTabHelper::RenderViewHostCreated(
226 content::RenderViewHost
* renderer
) {
227 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new
228 // RenderView, not RenderViewHost, and there is no good way to get
229 // notifications of RenderViewHosts. So just be tolerant of re-registrations.
230 bool registered
= registrar_
.IsRegistered(
232 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
233 content::Source
<RenderWidgetHost
>(renderer
));
237 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
238 content::Source
<RenderWidgetHost
>(renderer
));
242 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost
* widget
) {
245 UpdateThumbnailIfNecessary(web_contents());