• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
6 
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"
24 
25 #if defined(OS_WIN)
26 #include "base/win/windows_version.h"
27 #endif
28 
29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThumbnailTabHelper);
30 
31 class SkBitmap;
32 
33 // Overview
34 // --------
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
38 // simple as follows:
39 //
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().
45 
46 using content::RenderViewHost;
47 using content::RenderWidgetHost;
48 using content::WebContents;
49 
50 using thumbnails::ClipResult;
51 using thumbnails::ThumbnailingContext;
52 using thumbnails::ThumbnailingAlgorithm;
53 
54 namespace {
55 
56 // Feed the constructed thumbnail to the thumbnail service.
UpdateThumbnail(const ThumbnailingContext & context,const SkBitmap & thumbnail)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();
63 }
64 
ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context,scoped_refptr<ThumbnailingAlgorithm> algorithm,bool succeeded,const SkBitmap & bitmap)65 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context,
66                            scoped_refptr<ThumbnailingAlgorithm> algorithm,
67                            bool succeeded,
68                            const SkBitmap& bitmap) {
69   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
70   if (!succeeded)
71     return;
72 
73   algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
74 }
75 
GotSnapshotFromRenderer(base::Callback<void (const SkBitmap &)> callback,bool success,const SkBitmap & bitmap)76 void GotSnapshotFromRenderer(base::Callback<void(const SkBitmap&)> callback,
77                              bool success,
78                              const SkBitmap& bitmap) {
79   if (success)
80     callback.Run(bitmap);
81 }
82 
AsyncProcessThumbnail(content::WebContents * web_contents,scoped_refptr<ThumbnailingContext> context,scoped_refptr<ThumbnailingAlgorithm> algorithm)83 void AsyncProcessThumbnail(content::WebContents* web_contents,
84                            scoped_refptr<ThumbnailingContext> context,
85                            scoped_refptr<ThumbnailingAlgorithm> algorithm) {
86   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
87   RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost();
88   content::RenderWidgetHostView* view = render_widget_host->GetView();
89   if (!view)
90     return;
91   if (!view->IsSurfaceAvailableForCopy()) {
92     // On Windows XP and possibly due to driver issues, neither the backing
93     // store nor the compositing surface is available in the browser when
94     // accelerated compositing is active, so ask the renderer to send a snapshot
95     // for creating the thumbnail.
96     render_widget_host->GetSnapshotFromRenderer(
97       gfx::Rect(),
98       base::Bind(GotSnapshotFromRenderer, base::Bind(
99           &ThumbnailingAlgorithm::ProcessBitmap,
100           algorithm, context, base::Bind(&UpdateThumbnail))));
101     return;
102   }
103 
104   gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
105   // Clip the pixels that will commonly hold a scrollbar, which looks bad in
106   // thumbnails.
107   int scrollbar_size = gfx::scrollbar_size();
108   gfx::Size copy_size;
109   copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
110 
111   if (copy_rect.IsEmpty())
112     return;
113 
114   context->clip_result = algorithm->GetCanvasCopyInfo(
115       copy_rect.size(),
116       ui::GetScaleFactorForNativeView(view->GetNativeView()),
117       &copy_rect,
118       &context->requested_copy_size);
119 
120   render_widget_host->CopyFromBackingStore(
121       copy_rect,
122       context->requested_copy_size,
123       base::Bind(&ProcessCapturedBitmap, context, algorithm));
124 }
125 
126 }  // namespace
127 
ThumbnailTabHelper(content::WebContents * contents)128 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
129     : content::WebContentsObserver(contents),
130       enabled_(true),
131       load_interrupted_(false) {
132   // Even though we deal in RenderWidgetHosts, we only care about its
133   // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
134   // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
135   // aren't views like select popups.
136   registrar_.Add(this,
137                  content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
138                  content::Source<WebContents>(contents));
139 }
140 
~ThumbnailTabHelper()141 ThumbnailTabHelper::~ThumbnailTabHelper() {
142 }
143 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)144 void ThumbnailTabHelper::Observe(int type,
145                                  const content::NotificationSource& source,
146                                  const content::NotificationDetails& details) {
147   switch (type) {
148     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED:
149       RenderViewHostCreated(content::Details<RenderViewHost>(details).ptr());
150       break;
151 
152     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED:
153       if (!*content::Details<bool>(details).ptr())
154         WidgetHidden(content::Source<RenderWidgetHost>(source).ptr());
155       break;
156 
157     default:
158       NOTREACHED() << "Unexpected notification type: " << type;
159   }
160 }
161 
RenderViewDeleted(content::RenderViewHost * render_view_host)162 void ThumbnailTabHelper::RenderViewDeleted(
163     content::RenderViewHost* render_view_host) {
164   g_browser_process->GetRenderWidgetSnapshotTaker()->CancelSnapshot(
165       render_view_host);
166 
167   bool registered = registrar_.IsRegistered(
168       this,
169       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
170       content::Source<RenderWidgetHost>(render_view_host));
171   if (registered) {
172     registrar_.Remove(
173         this,
174         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
175         content::Source<RenderWidgetHost>(render_view_host));
176   }
177 }
178 
DidStartLoading(content::RenderViewHost * render_view_host)179 void ThumbnailTabHelper::DidStartLoading(
180     content::RenderViewHost* render_view_host) {
181   load_interrupted_ = false;
182 }
183 
NavigationStopped()184 void ThumbnailTabHelper::NavigationStopped() {
185   // This function gets called when the page loading is interrupted by the
186   // stop button.
187   load_interrupted_ = true;
188 }
189 
UpdateThumbnailIfNecessary(WebContents * web_contents)190 void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
191     WebContents* web_contents) {
192   // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
193   // which would be unwise to attempt <http://crbug.com/130097>. If the
194   // WebContents is in the middle of destruction, do not risk it.
195   if (!web_contents || web_contents->IsBeingDestroyed())
196     return;
197   // Skip if a pending entry exists. WidgetHidden can be called while navigating
198   // pages and this is not a time when thumbnails should be generated.
199   if (web_contents->GetController().GetPendingEntry())
200     return;
201   const GURL& url = web_contents->GetURL();
202   Profile* profile =
203       Profile::FromBrowserContext(web_contents->GetBrowserContext());
204 
205   scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
206       ThumbnailServiceFactory::GetForProfile(profile);
207 
208   // Skip if we don't need to update the thumbnail.
209   if (thumbnail_service.get() == NULL ||
210       !thumbnail_service->ShouldAcquirePageThumbnail(url)) {
211     return;
212   }
213 
214   scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
215       thumbnail_service->GetThumbnailingAlgorithm());
216 
217   scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
218       web_contents, thumbnail_service.get(), load_interrupted_));
219   AsyncProcessThumbnail(web_contents, context, algorithm);
220 }
221 
RenderViewHostCreated(content::RenderViewHost * renderer)222 void ThumbnailTabHelper::RenderViewHostCreated(
223     content::RenderViewHost* renderer) {
224   // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new
225   // RenderView, not RenderViewHost, and there is no good way to get
226   // notifications of RenderViewHosts. So just be tolerant of re-registrations.
227   bool registered = registrar_.IsRegistered(
228       this,
229       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
230       content::Source<RenderWidgetHost>(renderer));
231   if (!registered) {
232     registrar_.Add(
233         this,
234         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
235         content::Source<RenderWidgetHost>(renderer));
236   }
237 }
238 
WidgetHidden(RenderWidgetHost * widget)239 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) {
240   if (!enabled_)
241     return;
242   UpdateThumbnailIfNecessary(web_contents());
243 }
244