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 "chrome/browser/browser_process.h" | 7 #include "chrome/browser/browser_process.h" |
8 #include "chrome/browser/profiles/profile.h" | 8 #include "chrome/browser/profiles/profile.h" |
9 #include "chrome/browser/thumbnails/thumbnail_service.h" | 9 #include "chrome/browser/thumbnails/thumbnail_service.h" |
10 #include "chrome/browser/thumbnails/thumbnail_service_factory.h" | 10 #include "chrome/browser/thumbnails/thumbnail_service_factory.h" |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
47 using content::RenderViewHost; | 47 using content::RenderViewHost; |
48 using content::RenderWidgetHost; | 48 using content::RenderWidgetHost; |
49 using content::WebContents; | 49 using content::WebContents; |
50 | 50 |
51 using thumbnails::ClipResult; | 51 using thumbnails::ClipResult; |
52 using thumbnails::ThumbnailingContext; | 52 using thumbnails::ThumbnailingContext; |
53 using thumbnails::ThumbnailingAlgorithm; | 53 using thumbnails::ThumbnailingAlgorithm; |
54 | 54 |
55 namespace { | 55 namespace { |
56 | 56 |
57 // Balance the call to IncrementCapturerCount() from | |
58 // UpdateThumbnailIfNecessary(), and inform the tab helper that thumbnail | |
59 // generation is complete. | |
60 void DecrementCapturerCount(const ThumbnailingContext& context) { | |
Lei Zhang
2015/11/20 20:01:04
If you make these ThumbnailTabHelper methods, and
Lei Zhang
2016/01/07 22:15:29
I just reiterated this idea in person. :)
shrike
2016/01/08 18:32:36
I think I found a way around having to hand the th
| |
61 if (context.web_contents()) { | |
62 context.web_contents()->DecrementCapturerCount(); | |
63 } | |
64 // When ProcessCapturedBitmap receives a failed response, and that failure | |
65 // is due to shutdown, the current thread is not the UI thread. However the | |
66 // tab helper weakptr can only be dereferenced from the UI thread. | |
67 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { | |
68 return; | |
69 } | |
70 ThumbnailTabHelper* tab_helper = context.source_tab_helper().get(); | |
71 if (tab_helper) { | |
72 tab_helper->set_thumbnail_generation_in_progress(false); | |
73 } | |
74 } | |
75 | |
57 // Feed the constructed thumbnail to the thumbnail service. | 76 // Feed the constructed thumbnail to the thumbnail service. |
58 void UpdateThumbnail(const ThumbnailingContext& context, | 77 void UpdateThumbnail(const ThumbnailingContext& context, |
59 const SkBitmap& thumbnail) { | 78 const SkBitmap& thumbnail) { |
79 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
60 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); | 80 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); |
61 context.service->SetPageThumbnail(context, image); | 81 context.service()->SetPageThumbnail(context, image); |
62 DVLOG(1) << "Thumbnail taken for " << context.url << ": " | 82 DVLOG(1) << "Thumbnail taken for " << context.GetURL() << ": " |
63 << context.score.ToString(); | 83 << context.score().ToString(); |
84 | |
85 DecrementCapturerCount(context); | |
64 } | 86 } |
65 | 87 |
66 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context, | 88 void ProcessCapturedBitmap(scoped_refptr<ThumbnailingContext> context, |
67 scoped_refptr<ThumbnailingAlgorithm> algorithm, | 89 scoped_refptr<ThumbnailingAlgorithm> algorithm, |
68 const SkBitmap& bitmap, | 90 const SkBitmap& bitmap, |
69 content::ReadbackResponse response) { | 91 content::ReadbackResponse response) { |
70 if (response != content::READBACK_SUCCESS) | 92 if (context->web_contents() && response == content::READBACK_SUCCESS) { |
71 return; | 93 // On success, we must be on the UI thread (on failure because of shutdown |
72 | 94 // we are not on the UI thread). |
73 // On success, we must be on the UI thread (on failure because of shutdown we | 95 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
74 // are not on the UI thread). | 96 algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap); |
75 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 97 } else { |
76 | 98 DecrementCapturerCount(*context); |
77 algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap); | 99 } |
78 } | 100 } |
79 | 101 |
80 void AsyncProcessThumbnail(content::WebContents* web_contents, | 102 void AsyncProcessThumbnail(scoped_refptr<ThumbnailingContext> context, |
81 scoped_refptr<ThumbnailingContext> context, | |
82 scoped_refptr<ThumbnailingAlgorithm> algorithm) { | 103 scoped_refptr<ThumbnailingAlgorithm> algorithm) { |
83 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 104 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
84 RenderWidgetHost* render_widget_host = | 105 RenderWidgetHost* render_widget_host = |
85 web_contents->GetRenderViewHost()->GetWidget(); | 106 context->web_contents()->GetRenderViewHost()->GetWidget(); |
86 content::RenderWidgetHostView* view = render_widget_host->GetView(); | 107 content::RenderWidgetHostView* view = render_widget_host->GetView(); |
87 if (!view) | 108 if (!view) |
88 return; | 109 return; |
89 if (!view->IsSurfaceAvailableForCopy()) | 110 if (!view->IsSurfaceAvailableForCopy()) |
90 return; | 111 return; |
91 | 112 |
92 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); | 113 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); |
93 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | 114 // Clip the pixels that will commonly hold a scrollbar, which looks bad in |
94 // thumbnails. | 115 // thumbnails. |
95 int scrollbar_size = gfx::scrollbar_size(); | 116 int scrollbar_size = gfx::scrollbar_size(); |
96 gfx::Size copy_size; | 117 gfx::Size copy_size; |
97 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); | 118 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); |
98 | 119 |
99 if (copy_rect.IsEmpty()) | 120 if (copy_rect.IsEmpty()) |
100 return; | 121 return; |
101 | 122 |
102 ui::ScaleFactor scale_factor = | 123 ui::ScaleFactor scale_factor = |
103 ui::GetSupportedScaleFactor( | 124 ui::GetSupportedScaleFactor( |
104 ui::GetScaleFactorForNativeView(view->GetNativeView())); | 125 ui::GetScaleFactorForNativeView(view->GetNativeView())); |
105 context->clip_result = algorithm->GetCanvasCopyInfo( | 126 gfx::Size requested_copy_size; |
127 context->set_clip_result(algorithm->GetCanvasCopyInfo( | |
106 copy_rect.size(), | 128 copy_rect.size(), |
107 scale_factor, | 129 scale_factor, |
108 ©_rect, | 130 ©_rect, |
109 &context->requested_copy_size); | 131 &requested_copy_size)); |
132 context->set_requested_copy_size(requested_copy_size); | |
110 render_widget_host->CopyFromBackingStore( | 133 render_widget_host->CopyFromBackingStore( |
111 copy_rect, | 134 copy_rect, |
112 context->requested_copy_size, | 135 requested_copy_size, |
113 base::Bind(&ProcessCapturedBitmap, context, algorithm), | 136 base::Bind(&ProcessCapturedBitmap, context, algorithm), |
114 kN32_SkColorType); | 137 kN32_SkColorType); |
115 } | 138 } |
116 | 139 |
117 } // namespace | 140 } // namespace |
118 | 141 |
119 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) | 142 ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents) |
120 : content::WebContentsObserver(contents), | 143 : content::WebContentsObserver(contents), |
121 load_interrupted_(false) { | 144 load_interrupted_(false), |
145 thumbnail_generation_in_progress_(false), | |
146 weak_factory_(this) { | |
122 // Even though we deal in RenderWidgetHosts, we only care about its | 147 // Even though we deal in RenderWidgetHosts, we only care about its |
123 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails | 148 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails |
124 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that | 149 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that |
125 // aren't views like select popups. | 150 // aren't views like select popups. |
126 registrar_.Add(this, | 151 registrar_.Add(this, |
127 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, | 152 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
128 content::Source<WebContents>(contents)); | 153 content::Source<WebContents>(contents)); |
129 } | 154 } |
130 | 155 |
131 ThumbnailTabHelper::~ThumbnailTabHelper() { | 156 ThumbnailTabHelper::~ThumbnailTabHelper() { |
(...skipping 26 matching lines...) Expand all Loading... | |
158 registrar_.Remove( | 183 registrar_.Remove( |
159 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 184 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
160 content::Source<RenderWidgetHost>(render_view_host->GetWidget())); | 185 content::Source<RenderWidgetHost>(render_view_host->GetWidget())); |
161 } | 186 } |
162 } | 187 } |
163 | 188 |
164 void ThumbnailTabHelper::DidStartLoading() { | 189 void ThumbnailTabHelper::DidStartLoading() { |
165 load_interrupted_ = false; | 190 load_interrupted_ = false; |
166 } | 191 } |
167 | 192 |
193 void ThumbnailTabHelper::set_thumbnail_generation_in_progress(bool flag) { | |
Lei Zhang
2015/11/20 20:01:04
Since we only call this with flag = false, make it
shrike
2016/01/08 18:32:35
This method will go away in my revised patchset.
| |
194 thumbnail_generation_in_progress_ = flag; | |
195 } | |
196 | |
168 void ThumbnailTabHelper::NavigationStopped() { | 197 void ThumbnailTabHelper::NavigationStopped() { |
169 // This function gets called when the page loading is interrupted by the | 198 // This function gets called when the page loading is interrupted by the |
170 // stop button. | 199 // stop button. |
171 load_interrupted_ = true; | 200 load_interrupted_ = true; |
172 } | 201 } |
173 | 202 |
174 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( | 203 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( |
175 WebContents* web_contents) { | 204 WebContents* web_contents) { |
205 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
206 // Ignore thumbnail update requests if one is already being generated. | |
207 if (thumbnail_generation_in_progress_) { | |
208 return; | |
209 } | |
176 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot | 210 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot |
177 // which would be unwise to attempt <http://crbug.com/130097>. If the | 211 // which would be unwise to attempt <http://crbug.com/130097>. If the |
178 // WebContents is in the middle of destruction, do not risk it. | 212 // WebContents is in the middle of destruction, do not risk it. |
179 if (!web_contents || web_contents->IsBeingDestroyed()) | 213 if (!web_contents || web_contents->IsBeingDestroyed()) |
180 return; | 214 return; |
181 // Skip if a pending entry exists. WidgetHidden can be called while navigating | 215 // Skip if a pending entry exists. WidgetHidden can be called while navigating |
182 // pages and this is not a time when thumbnails should be generated. | 216 // pages and this is not a time when thumbnails should be generated. |
183 if (web_contents->GetController().GetPendingEntry()) | 217 if (web_contents->GetController().GetPendingEntry()) |
184 return; | 218 return; |
185 const GURL& url = web_contents->GetURL(); | 219 const GURL& url = web_contents->GetURL(); |
186 Profile* profile = | 220 Profile* profile = |
187 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 221 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
188 | 222 |
189 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = | 223 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = |
190 ThumbnailServiceFactory::GetForProfile(profile); | 224 ThumbnailServiceFactory::GetForProfile(profile); |
191 | 225 |
192 // Skip if we don't need to update the thumbnail. | 226 // Skip if we don't need to update the thumbnail. |
193 if (thumbnail_service.get() == NULL || | 227 if (thumbnail_service.get() == NULL || |
194 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { | 228 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { |
195 return; | 229 return; |
196 } | 230 } |
197 | 231 |
232 web_contents->IncrementCapturerCount(gfx::Size()); | |
233 // It turns out that the corresponding DecrementCapturerCount() can trigger a | |
234 // call to content::WebContentsImpl::WasHidden(), which eventually calls | |
235 // ThumbnailTabHelper::UpdateThumbnailIfNecessary(), starting the thumbnail | |
236 // generation process all over again. Break this cycle by noting that we're | |
237 // currently generating a thumbnail. | |
238 thumbnail_generation_in_progress_ = true; | |
198 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( | 239 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( |
199 thumbnail_service->GetThumbnailingAlgorithm()); | 240 thumbnail_service->GetThumbnailingAlgorithm()); |
200 | 241 |
201 scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext( | 242 scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext( |
202 web_contents, thumbnail_service.get(), load_interrupted_)); | 243 web_contents, thumbnail_service.get(), weak_factory_.GetWeakPtr(), |
203 AsyncProcessThumbnail(web_contents, context, algorithm); | 244 load_interrupted_)); |
245 AsyncProcessThumbnail(context, algorithm); | |
204 } | 246 } |
205 | 247 |
206 void ThumbnailTabHelper::RenderViewHostCreated( | 248 void ThumbnailTabHelper::RenderViewHostCreated( |
207 content::RenderViewHost* renderer) { | 249 content::RenderViewHost* renderer) { |
208 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new | 250 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new |
209 // RenderView, not RenderViewHost, and there is no good way to get | 251 // RenderView, not RenderViewHost, and there is no good way to get |
210 // notifications of RenderViewHosts. So just be tolerant of re-registrations. | 252 // notifications of RenderViewHosts. So just be tolerant of re-registrations. |
211 bool registered = registrar_.IsRegistered( | 253 bool registered = registrar_.IsRegistered( |
212 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 254 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
213 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 255 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
214 if (!registered) { | 256 if (!registered) { |
215 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 257 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
216 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 258 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
217 } | 259 } |
218 } | 260 } |
219 | 261 |
220 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { | 262 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { |
221 UpdateThumbnailIfNecessary(web_contents()); | 263 UpdateThumbnailIfNecessary(web_contents()); |
222 } | 264 } |
OLD | NEW |