Update To 11.40.268.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/browser_thread.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.
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   DVLOG(1) << "Thumbnail taken for " << context.url << ": "
62            << context.score.ToString();
63 }
64
65 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context,
66                            scoped_refptr<ThumbnailingAlgorithm> algorithm,
67                            bool succeeded,
68                            const SkBitmap& bitmap) {
69   if (!succeeded)
70     return;
71
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));
75
76   algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
77 }
78
79 void AsyncProcessThumbnail(content::WebContents* web_contents,
80                            scoped_refptr<ThumbnailingContext> context,
81                            scoped_refptr<ThumbnailingAlgorithm> algorithm) {
82   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
83   RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost();
84   content::RenderWidgetHostView* view = render_widget_host->GetView();
85   if (!view)
86     return;
87   if (!view->IsSurfaceAvailableForCopy())
88     return;
89
90   gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
91   // Clip the pixels that will commonly hold a scrollbar, which looks bad in
92   // thumbnails.
93   int scrollbar_size = gfx::scrollbar_size();
94   gfx::Size copy_size;
95   copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
96
97   if (copy_rect.IsEmpty())
98     return;
99
100   ui::ScaleFactor scale_factor =
101       ui::GetSupportedScaleFactor(
102           ui::GetScaleFactorForNativeView(view->GetNativeView()));
103   context->clip_result = algorithm->GetCanvasCopyInfo(
104       copy_rect.size(),
105       scale_factor,
106       &copy_rect,
107       &context->requested_copy_size);
108   render_widget_host->CopyFromBackingStore(
109       copy_rect,
110       context->requested_copy_size,
111       base::Bind(&ProcessCapturedBitmap, context, algorithm),
112       kN32_SkColorType);
113 }
114
115 }  // namespace
116
117 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
118     : content::WebContentsObserver(contents),
119       enabled_(true),
120       load_interrupted_(false) {
121   // Even though we deal in RenderWidgetHosts, we only care about its
122   // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
123   // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
124   // aren't views like select popups.
125   registrar_.Add(this,
126                  content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
127                  content::Source<WebContents>(contents));
128 }
129
130 ThumbnailTabHelper::~ThumbnailTabHelper() {
131 }
132
133 void ThumbnailTabHelper::Observe(int type,
134                                  const content::NotificationSource& source,
135                                  const content::NotificationDetails& details) {
136   switch (type) {
137     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED:
138       RenderViewHostCreated(content::Details<RenderViewHost>(details).ptr());
139       break;
140
141     case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED:
142       if (!*content::Details<bool>(details).ptr())
143         WidgetHidden(content::Source<RenderWidgetHost>(source).ptr());
144       break;
145
146     default:
147       NOTREACHED() << "Unexpected notification type: " << type;
148   }
149 }
150
151 void ThumbnailTabHelper::RenderViewDeleted(
152     content::RenderViewHost* render_view_host) {
153   bool registered = registrar_.IsRegistered(
154       this,
155       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
156       content::Source<RenderWidgetHost>(render_view_host));
157   if (registered) {
158     registrar_.Remove(
159         this,
160         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
161         content::Source<RenderWidgetHost>(render_view_host));
162   }
163 }
164
165 void ThumbnailTabHelper::DidStartLoading(
166     content::RenderViewHost* render_view_host) {
167   load_interrupted_ = false;
168 }
169
170 void ThumbnailTabHelper::NavigationStopped() {
171   // This function gets called when the page loading is interrupted by the
172   // stop button.
173   load_interrupted_ = true;
174 }
175
176 void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
177     WebContents* web_contents) {
178   // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
179   // which would be unwise to attempt <http://crbug.com/130097>. If the
180   // WebContents is in the middle of destruction, do not risk it.
181   if (!web_contents || web_contents->IsBeingDestroyed())
182     return;
183   // Skip if a pending entry exists. WidgetHidden can be called while navigating
184   // pages and this is not a time when thumbnails should be generated.
185   if (web_contents->GetController().GetPendingEntry())
186     return;
187   const GURL& url = web_contents->GetURL();
188   Profile* profile =
189       Profile::FromBrowserContext(web_contents->GetBrowserContext());
190
191   scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
192       ThumbnailServiceFactory::GetForProfile(profile);
193
194   // Skip if we don't need to update the thumbnail.
195   if (thumbnail_service.get() == NULL ||
196       !thumbnail_service->ShouldAcquirePageThumbnail(url)) {
197     return;
198   }
199
200   scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
201       thumbnail_service->GetThumbnailingAlgorithm());
202
203   scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext(
204       web_contents, thumbnail_service.get(), load_interrupted_));
205   AsyncProcessThumbnail(web_contents, context, algorithm);
206 }
207
208 void ThumbnailTabHelper::RenderViewHostCreated(
209     content::RenderViewHost* renderer) {
210   // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new
211   // RenderView, not RenderViewHost, and there is no good way to get
212   // notifications of RenderViewHosts. So just be tolerant of re-registrations.
213   bool registered = registrar_.IsRegistered(
214       this,
215       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
216       content::Source<RenderWidgetHost>(renderer));
217   if (!registered) {
218     registrar_.Add(
219         this,
220         content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
221         content::Source<RenderWidgetHost>(renderer));
222   }
223 }
224
225 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) {
226   if (!enabled_)
227     return;
228   UpdateThumbnailIfNecessary(web_contents());
229 }