Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / thumbnails / thumbnail_tab_helper.cc
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/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"
23
24 #if defined(OS_WIN)
25 #include "base/win/windows_version.h"
26 #endif
27
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ThumbnailTabHelper);
29
30 class SkBitmap;
31
32 // Overview
33 // --------
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
37 // simple as follows:
38 //
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().
44
45 using content::RenderViewHost;
46 using content::RenderWidgetHost;
47 using content::WebContents;
48
49 using thumbnails::ClipResult;
50 using thumbnails::ThumbnailingContext;
51 using thumbnails::ThumbnailingAlgorithm;
52
53 namespace {
54
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();
62 }
63
64 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context,
65                            scoped_refptr<ThumbnailingAlgorithm> algorithm,
66                            bool succeeded,
67                            const SkBitmap& bitmap) {
68   if (!succeeded)
69     return;
70
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));
74
75   algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
76 }
77
78 void GotSnapshotFromRenderer(base::Callback<void(const SkBitmap&)> callback,
79                              bool success,
80                              const SkBitmap& bitmap) {
81   if (success)
82     callback.Run(bitmap);
83 }
84
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();
91   if (!view)
92     return;
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(
99       gfx::Rect(),
100       base::Bind(GotSnapshotFromRenderer, base::Bind(
101           &ThumbnailingAlgorithm::ProcessBitmap,
102           algorithm, context, base::Bind(&UpdateThumbnail))));
103     return;
104   }
105
106   gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
107   // Clip the pixels that will commonly hold a scrollbar, which looks bad in
108   // thumbnails.
109   int scrollbar_size = gfx::scrollbar_size();
110   gfx::Size copy_size;
111   copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
112
113   if (copy_rect.IsEmpty())
114     return;
115
116   context->clip_result = algorithm->GetCanvasCopyInfo(
117       copy_rect.size(),
118       ui::GetScaleFactorForNativeView(view->GetNativeView()),
119       &copy_rect,
120       &context->requested_copy_size);
121
122   render_widget_host->CopyFromBackingStore(
123       copy_rect,
124       context->requested_copy_size,
125       base::Bind(&ProcessCapturedBitmap, context, algorithm));
126 }
127
128 }  // namespace
129
130 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
131     : content::WebContentsObserver(contents),
132       enabled_(true),
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.
138   registrar_.Add(this,
139                  content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
140                  content::Source<WebContents>(contents));
141 }
142
143 ThumbnailTabHelper::~ThumbnailTabHelper() {
144 }
145
146 void ThumbnailTabHelper::Observe(int type,
147                                  const content::NotificationSource& source,
148                                  const content::NotificationDetails& details) {
149   switch (type) {
150     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED:
151       RenderViewHostCreated(content::Details<RenderViewHost>(details).ptr());
152       break;
153
154     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED:
155       if (!*content::Details<bool>(details).ptr())
156         WidgetHidden(content::Source<RenderWidgetHost>(source).ptr());
157       break;
158
159     default:
160       NOTREACHED() << "Unexpected notification type: " << type;
161   }
162 }
163
164 void ThumbnailTabHelper::RenderViewDeleted(
165     content::RenderViewHost* render_view_host) {
166   bool registered = registrar_.IsRegistered(
167       this,
168       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
169       content::Source<RenderWidgetHost>(render_view_host));
170   if (registered) {
171     registrar_.Remove(
172         this,
173         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
174         content::Source<RenderWidgetHost>(render_view_host));
175   }
176 }
177
178 void ThumbnailTabHelper::DidStartLoading(
179     content::RenderViewHost* render_view_host) {
180   load_interrupted_ = false;
181 }
182
183 void ThumbnailTabHelper::NavigationStopped() {
184   // This function gets called when the page loading is interrupted by the
185   // stop button.
186   load_interrupted_ = true;
187 }
188
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())
195     return;
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())
199     return;
200   const GURL& url = web_contents->GetURL();
201   Profile* profile =
202       Profile::FromBrowserContext(web_contents->GetBrowserContext());
203
204   scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
205       ThumbnailServiceFactory::GetForProfile(profile);
206
207   // Skip if we don't need to update the thumbnail.
208   if (thumbnail_service.get() == NULL ||
209       !thumbnail_service->ShouldAcquirePageThumbnail(url)) {
210     return;
211   }
212
213   scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
214       thumbnail_service->GetThumbnailingAlgorithm());
215
216   scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
217       web_contents, thumbnail_service.get(), load_interrupted_));
218   AsyncProcessThumbnail(web_contents, context, algorithm);
219 }
220
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(
227       this,
228       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
229       content::Source<RenderWidgetHost>(renderer));
230   if (!registered) {
231     registrar_.Add(
232         this,
233         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
234         content::Source<RenderWidgetHost>(renderer));
235   }
236 }
237
238 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) {
239   if (!enabled_)
240     return;
241   UpdateThumbnailIfNecessary(web_contents());
242 }