OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" | 5 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h" |
6 | 6 |
7 #include "build/build_config.h" | 7 #include "build/build_config.h" |
8 #include "chrome/browser/browser_process.h" | 8 #include "chrome/browser/browser_process.h" |
9 #include "chrome/browser/profiles/profile.h" | 9 #include "chrome/browser/profiles/profile.h" |
10 #include "chrome/browser/thumbnails/thumbnail_service.h" | 10 #include "chrome/browser/thumbnails/thumbnail_service.h" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
46 // implemented in ShouldUpdateThumbnail(). | 46 // implemented in ShouldUpdateThumbnail(). |
47 | 47 |
48 using content::RenderViewHost; | 48 using content::RenderViewHost; |
49 using content::RenderWidgetHost; | 49 using content::RenderWidgetHost; |
50 using content::WebContents; | 50 using content::WebContents; |
51 | 51 |
52 using thumbnails::ClipResult; | 52 using thumbnails::ClipResult; |
53 using thumbnails::ThumbnailingContext; | 53 using thumbnails::ThumbnailingContext; |
54 using thumbnails::ThumbnailingAlgorithm; | 54 using thumbnails::ThumbnailingAlgorithm; |
55 | 55 |
56 namespace { | |
57 | |
58 // Feed the constructed thumbnail to the thumbnail service. | |
59 void UpdateThumbnail(const ThumbnailingContext& context, | |
60 const SkBitmap& thumbnail) { | |
61 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); | |
62 context.service->SetPageThumbnail(context, image); | |
63 DVLOG(1) << "Thumbnail taken for " << context.url << ": " | |
64 << context.score.ToString(); | |
65 } | |
66 | |
67 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context, | |
68 scoped_refptr<ThumbnailingAlgorithm> algorithm, | |
69 const SkBitmap& bitmap, | |
70 content::ReadbackResponse response) { | |
71 if (response != content::READBACK_SUCCESS) | |
72 return; | |
73 | |
74 // On success, we must be on the UI thread (on failure because of shutdown we | |
75 // are not on the UI thread). | |
76 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
77 | |
78 algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap); | |
79 } | |
80 | |
81 void AsyncProcessThumbnail(content::WebContents* web_contents, | |
82 scoped_refptr<ThumbnailingContext> context, | |
83 scoped_refptr<ThumbnailingAlgorithm> algorithm) { | |
84 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
85 RenderWidgetHost* render_widget_host = | |
86 web_contents->GetRenderViewHost()->GetWidget(); | |
87 content::RenderWidgetHostView* view = render_widget_host->GetView(); | |
88 if (!view) | |
89 return; | |
90 if (!view->IsSurfaceAvailableForCopy()) | |
91 return; | |
92 | |
93 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); | |
94 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | |
95 // thumbnails. | |
96 int scrollbar_size = gfx::scrollbar_size(); | |
97 gfx::Size copy_size; | |
98 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); | |
99 | |
100 if (copy_rect.IsEmpty()) | |
101 return; | |
102 | |
103 ui::ScaleFactor scale_factor = | |
104 ui::GetSupportedScaleFactor( | |
105 ui::GetScaleFactorForNativeView(view->GetNativeView())); | |
106 context->clip_result = algorithm->GetCanvasCopyInfo( | |
107 copy_rect.size(), | |
108 scale_factor, | |
109 ©_rect, | |
110 &context->requested_copy_size); | |
111 render_widget_host->CopyFromBackingStore( | |
112 copy_rect, | |
113 context->requested_copy_size, | |
114 base::Bind(&ProcessCapturedBitmap, context, algorithm), | |
115 kN32_SkColorType); | |
116 } | |
117 | |
118 } // namespace | |
119 | |
120 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) | 56 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) |
121 : content::WebContentsObserver(contents), | 57 : content::WebContentsObserver(contents), |
122 load_interrupted_(false) { | 58 load_interrupted_(false), |
59 weak_factory_(this) { | |
123 // Even though we deal in RenderWidgetHosts, we only care about its | 60 // Even though we deal in RenderWidgetHosts, we only care about its |
124 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails | 61 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails |
125 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that | 62 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that |
126 // aren't views like select popups. | 63 // aren't views like select popups. |
127 registrar_.Add(this, | 64 registrar_.Add(this, |
128 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, | 65 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
129 content::Source<WebContents>(contents)); | 66 content::Source<WebContents>(contents)); |
130 } | 67 } |
131 | 68 |
132 ThumbnailTabHelper::~ThumbnailTabHelper() { | 69 ThumbnailTabHelper::~ThumbnailTabHelper() { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 void ThumbnailTabHelper::DidStartLoading() { | 102 void ThumbnailTabHelper::DidStartLoading() { |
166 load_interrupted_ = false; | 103 load_interrupted_ = false; |
167 } | 104 } |
168 | 105 |
169 void ThumbnailTabHelper::NavigationStopped() { | 106 void ThumbnailTabHelper::NavigationStopped() { |
170 // This function gets called when the page loading is interrupted by the | 107 // This function gets called when the page loading is interrupted by the |
171 // stop button. | 108 // stop button. |
172 load_interrupted_ = true; | 109 load_interrupted_ = true; |
173 } | 110 } |
174 | 111 |
175 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( | 112 void ThumbnailTabHelper::CleanUpFromThumbnailGeneration() { |
176 WebContents* web_contents) { | 113 if (web_contents()) { |
114 // Balance the call to IncrementCapturerCount() made in | |
115 // UpdateThumbnailIfNecessary(). | |
116 web_contents()->DecrementCapturerCount(); | |
117 } | |
118 | |
119 // Make a note that thumbnail generation is complete. | |
120 thumbnailing_context_ = nullptr; | |
121 } | |
122 | |
123 void ThumbnailTabHelper::UpdateThumbnail( | |
124 const thumbnails::ThumbnailingContext& context, | |
125 const SkBitmap& thumbnail) { | |
126 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
127 // Feed the constructed thumbnail to the thumbnail service. | |
128 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); | |
129 context.service->SetPageThumbnail(context, image); | |
Lei Zhang
2016/01/12 02:18:43
And funny indentation here.
shrike
2016/01/12 19:52:39
Thanks for catching that.
| |
130 DVLOG(1) << "Thumbnail taken for " << context.url << ": " | |
131 << context.score.ToString(); | |
132 | |
133 CleanUpFromThumbnailGeneration(); | |
134 } | |
135 | |
136 void ThumbnailTabHelper::ProcessCapturedBitmap( | |
137 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm, | |
138 const SkBitmap& bitmap, | |
139 content::ReadbackResponse response) { | |
140 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
Lei Zhang
2016/01/12 01:54:46
Didn't you have problems with this on shutdown, or
shrike
2016/01/12 19:52:39
This comment is from another engineer - I never en
| |
141 if (web_contents() && response == content::READBACK_SUCCESS) { | |
142 // On success, we must be on the UI thread (on failure because of shutdown | |
143 // we are not on the UI thread). | |
144 algorithm->ProcessBitmap(thumbnailing_context_, | |
145 base::Bind(&ThumbnailTabHelper::UpdateThumbnail, | |
146 weak_factory_.GetWeakPtr()), | |
147 bitmap); | |
148 } else { | |
149 CleanUpFromThumbnailGeneration(); | |
150 } | |
151 } | |
152 | |
153 void ThumbnailTabHelper::AsyncProcessThumbnail( | |
154 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service) { | |
155 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
156 RenderWidgetHost* render_widget_host = | |
157 web_contents()->GetRenderViewHost()->GetWidget(); | |
158 content::RenderWidgetHostView* view = render_widget_host->GetView(); | |
159 if (!view || !view->IsSurfaceAvailableForCopy()) { | |
160 return; | |
161 } | |
162 | |
163 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); | |
164 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | |
165 // thumbnails. | |
166 int scrollbar_size = gfx::scrollbar_size(); | |
167 gfx::Size copy_size; | |
168 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); | |
169 | |
170 if (copy_rect.IsEmpty()) { | |
171 return; | |
172 } | |
173 | |
174 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( | |
175 thumbnail_service->GetThumbnailingAlgorithm()); | |
176 | |
177 thumbnailing_context_ = new ThumbnailingContext(web_contents(), | |
178 thumbnail_service.get(), | |
179 load_interrupted_); | |
180 | |
181 ui::ScaleFactor scale_factor = | |
182 ui::GetSupportedScaleFactor( | |
183 ui::GetScaleFactorForNativeView(view->GetNativeView())); | |
184 thumbnailing_context_->clip_result = algorithm->GetCanvasCopyInfo( | |
185 copy_rect.size(), | |
186 scale_factor, | |
187 ©_rect, | |
188 &thumbnailing_context_->requested_copy_size); | |
189 render_widget_host->CopyFromBackingStore( | |
190 copy_rect, | |
191 thumbnailing_context_->requested_copy_size, | |
192 base::Bind(&ThumbnailTabHelper::ProcessCapturedBitmap, | |
193 weak_factory_.GetWeakPtr(), | |
194 algorithm), | |
195 kN32_SkColorType); | |
196 } | |
197 | |
198 void ThumbnailTabHelper::UpdateThumbnailIfNecessary() { | |
199 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
200 // Ignore thumbnail update requests if one is already in progress. This can | |
201 // happen at the end of thumbnail generation when | |
202 // CleanUpFromThumbnailGeneration() calls DecrementCapturerCount(), triggering | |
203 // a call to content::WebContentsImpl::WasHidden() which eventually calls | |
204 // ThumbnailTabHelper::UpdateThumbnailIfNecessary(), starting the thumbnail | |
205 // generation process all over again. | |
206 if (thumbnailing_context_) { | |
207 return; | |
208 } | |
209 | |
210 WebContents* the_web_contents = web_contents(); | |
Lei Zhang
2016/01/12 01:54:46
Just call web_contents() as needed below? At least
shrike
2016/01/12 19:52:39
OK.
| |
211 | |
177 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot | 212 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot |
178 // which would be unwise to attempt <http://crbug.com/130097>. If the | 213 // which would be unwise to attempt <http://crbug.com/130097>. If the |
179 // WebContents is in the middle of destruction, do not risk it. | 214 // WebContents is in the middle of destruction, do not risk it. |
180 if (!web_contents || web_contents->IsBeingDestroyed()) | 215 if (!the_web_contents || the_web_contents->IsBeingDestroyed()) |
181 return; | 216 return; |
182 // Skip if a pending entry exists. WidgetHidden can be called while navigating | 217 // Skip if a pending entry exists. WidgetHidden can be called while navigating |
183 // pages and this is not a time when thumbnails should be generated. | 218 // pages and this is not a time when thumbnails should be generated. |
184 if (web_contents->GetController().GetPendingEntry()) | 219 if (the_web_contents->GetController().GetPendingEntry()) |
185 return; | 220 return; |
186 const GURL& url = web_contents->GetURL(); | 221 const GURL& url = the_web_contents->GetURL(); |
187 Profile* profile = | 222 Profile* profile = |
188 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 223 Profile::FromBrowserContext(the_web_contents->GetBrowserContext()); |
189 | 224 |
190 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = | 225 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = |
191 ThumbnailServiceFactory::GetForProfile(profile); | 226 ThumbnailServiceFactory::GetForProfile(profile); |
192 | 227 |
193 // Skip if we don't need to update the thumbnail. | 228 // Skip if we don't need to update the thumbnail. |
194 if (thumbnail_service.get() == NULL || | 229 if (thumbnail_service.get() == NULL || |
195 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { | 230 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { |
196 return; | 231 return; |
197 } | 232 } |
198 | 233 |
199 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( | 234 // Prevent the web contents from disappearing before the async thumbnail |
200 thumbnail_service->GetThumbnailingAlgorithm()); | 235 // generation code executes. See https://crbug.com/530707 . |
236 the_web_contents->IncrementCapturerCount(gfx::Size()); | |
201 | 237 |
202 scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext( | 238 AsyncProcessThumbnail(thumbnail_service); |
203 web_contents, thumbnail_service.get(), load_interrupted_)); | |
204 AsyncProcessThumbnail(web_contents, context, algorithm); | |
205 } | 239 } |
206 | 240 |
207 void ThumbnailTabHelper::RenderViewHostCreated( | 241 void ThumbnailTabHelper::RenderViewHostCreated( |
208 content::RenderViewHost* renderer) { | 242 content::RenderViewHost* renderer) { |
209 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new | 243 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new |
210 // RenderView, not RenderViewHost, and there is no good way to get | 244 // RenderView, not RenderViewHost, and there is no good way to get |
211 // notifications of RenderViewHosts. So just be tolerant of re-registrations. | 245 // notifications of RenderViewHosts. So just be tolerant of re-registrations. |
212 bool registered = registrar_.IsRegistered( | 246 bool registered = registrar_.IsRegistered( |
213 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 247 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
214 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 248 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
215 if (!registered) { | 249 if (!registered) { |
216 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 250 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
217 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 251 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
218 } | 252 } |
219 } | 253 } |
220 | 254 |
221 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { | 255 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { |
222 UpdateThumbnailIfNecessary(web_contents()); | 256 UpdateThumbnailIfNecessary(); |
223 } | 257 } |
OLD | NEW |