Chromium Code Reviews| 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 thumbnailing_context_(nullptr), |
|
Lei Zhang
2016/01/09 02:14:24
no need to explicitly initialize scoped_refptrs.
shrike
2016/01/11 19:07:08
Acknowledged.
| |
| 59 load_interrupted_(false), | |
| 60 weak_factory_(this) { | |
| 123 // Even though we deal in RenderWidgetHosts, we only care about its | 61 // 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 | 62 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails |
| 125 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that | 63 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that |
| 126 // aren't views like select popups. | 64 // aren't views like select popups. |
| 127 registrar_.Add(this, | 65 registrar_.Add(this, |
| 128 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, | 66 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
| 129 content::Source<WebContents>(contents)); | 67 content::Source<WebContents>(contents)); |
| 130 } | 68 } |
| 131 | 69 |
| 132 ThumbnailTabHelper::~ThumbnailTabHelper() { | 70 ThumbnailTabHelper::~ThumbnailTabHelper() { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 void ThumbnailTabHelper::DidStartLoading() { | 103 void ThumbnailTabHelper::DidStartLoading() { |
| 166 load_interrupted_ = false; | 104 load_interrupted_ = false; |
| 167 } | 105 } |
| 168 | 106 |
| 169 void ThumbnailTabHelper::NavigationStopped() { | 107 void ThumbnailTabHelper::NavigationStopped() { |
| 170 // This function gets called when the page loading is interrupted by the | 108 // This function gets called when the page loading is interrupted by the |
| 171 // stop button. | 109 // stop button. |
| 172 load_interrupted_ = true; | 110 load_interrupted_ = true; |
| 173 } | 111 } |
| 174 | 112 |
| 113 // Balance the call to IncrementCapturerCount() from | |
|
Lei Zhang
2016/01/09 02:14:24
Comments go in the header.
shrike
2016/01/11 19:07:08
Acknowledged.
| |
| 114 // UpdateThumbnailIfNecessary(), and inform the tab helper that thumbnail | |
| 115 // generation is complete. | |
| 116 void ThumbnailTabHelper::DecrementCapturerCount() { | |
| 117 if (web_contents()) { | |
| 118 web_contents()->DecrementCapturerCount(); | |
| 119 } | |
| 120 thumbnailing_context_ = nullptr; | |
| 121 } | |
| 122 | |
| 123 // Feed the constructed thumbnail to the thumbnail service. | |
| 124 void ThumbnailTabHelper::UpdateThumbnail( | |
| 125 const thumbnails::ThumbnailingContext& context, | |
| 126 const SkBitmap& thumbnail) { | |
| 127 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 128 gfx::Image image = gfx::Image::CreateFrom1xBitmap(thumbnail); | |
| 129 context.service->SetPageThumbnail(context, image); | |
| 130 DVLOG(1) << "Thumbnail taken for " << context.url << ": " | |
| 131 << context.score.ToString(); | |
| 132 | |
| 133 DecrementCapturerCount(); | |
| 134 } | |
| 135 | |
| 136 void ThumbnailTabHelper::ProcessCapturedBitmap( | |
| 137 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm, | |
| 138 const SkBitmap& bitmap, | |
| 139 content::ReadbackResponse response) { | |
| 140 if (web_contents() && response == content::READBACK_SUCCESS) { | |
| 141 // On success, we must be on the UI thread (on failure because of shutdown | |
| 142 // we are not on the UI thread). | |
| 143 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 144 algorithm->ProcessBitmap( | |
| 145 thumbnailing_context_, | |
| 146 base::Bind(&ThumbnailTabHelper::UpdateThumbnail, | |
| 147 weak_factory_.GetWeakPtr()), | |
| 148 bitmap); | |
| 149 } else { | |
| 150 DecrementCapturerCount(); | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 void ThumbnailTabHelper::AsyncProcessThumbnail( | |
| 155 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm) { | |
| 156 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 157 RenderWidgetHost* render_widget_host = | |
| 158 web_contents()->GetRenderViewHost()->GetWidget(); | |
| 159 content::RenderWidgetHostView* view = render_widget_host->GetView(); | |
| 160 if (!view || !view->IsSurfaceAvailableForCopy()) { | |
| 161 thumbnailing_context_ = nullptr; | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size()); | |
| 166 // Clip the pixels that will commonly hold a scrollbar, which looks bad in | |
| 167 // thumbnails. | |
| 168 int scrollbar_size = gfx::scrollbar_size(); | |
| 169 gfx::Size copy_size; | |
| 170 copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size); | |
| 171 | |
| 172 if (copy_rect.IsEmpty()) { | |
| 173 thumbnailing_context_ = nullptr; | |
| 174 return; | |
| 175 } | |
| 176 | |
| 177 ui::ScaleFactor scale_factor = | |
| 178 ui::GetSupportedScaleFactor( | |
| 179 ui::GetScaleFactorForNativeView(view->GetNativeView())); | |
| 180 thumbnailing_context_->clip_result = algorithm->GetCanvasCopyInfo( | |
| 181 copy_rect.size(), | |
| 182 scale_factor, | |
| 183 ©_rect, | |
| 184 &thumbnailing_context_->requested_copy_size); | |
| 185 render_widget_host->CopyFromBackingStore( | |
| 186 copy_rect, | |
| 187 thumbnailing_context_->requested_copy_size, | |
| 188 base::Bind(&ThumbnailTabHelper::ProcessCapturedBitmap, | |
| 189 weak_factory_.GetWeakPtr(), | |
| 190 algorithm), | |
| 191 kN32_SkColorType); | |
| 192 } | |
| 193 | |
| 175 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( | 194 void ThumbnailTabHelper::UpdateThumbnailIfNecessary( |
| 176 WebContents* web_contents) { | 195 WebContents* web_contents) { |
|
Lei Zhang
2016/01/09 02:14:25
BTW, why bother having this? Just call web_content
| |
| 196 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
|
Lei Zhang
2016/01/09 02:14:24
I think you should add this everywhere.
shrike
2016/01/11 19:07:08
It pretty much already is everywhere.
| |
| 197 // Ignore thumbnail update requests if one is already being generated. | |
| 198 if (thumbnailing_context_) { | |
| 199 return; | |
| 200 } | |
| 177 // Destroying a WebContents may trigger it to be hidden, prompting a snapshot | 201 // 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 | 202 // which would be unwise to attempt <http://crbug.com/130097>. If the |
| 179 // WebContents is in the middle of destruction, do not risk it. | 203 // WebContents is in the middle of destruction, do not risk it. |
| 180 if (!web_contents || web_contents->IsBeingDestroyed()) | 204 if (!web_contents || web_contents->IsBeingDestroyed()) |
| 181 return; | 205 return; |
| 182 // Skip if a pending entry exists. WidgetHidden can be called while navigating | 206 // 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. | 207 // pages and this is not a time when thumbnails should be generated. |
| 184 if (web_contents->GetController().GetPendingEntry()) | 208 if (web_contents->GetController().GetPendingEntry()) |
| 185 return; | 209 return; |
| 186 const GURL& url = web_contents->GetURL(); | 210 const GURL& url = web_contents->GetURL(); |
| 187 Profile* profile = | 211 Profile* profile = |
| 188 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | 212 Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| 189 | 213 |
| 190 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = | 214 scoped_refptr<thumbnails::ThumbnailService> thumbnail_service = |
| 191 ThumbnailServiceFactory::GetForProfile(profile); | 215 ThumbnailServiceFactory::GetForProfile(profile); |
| 192 | 216 |
| 193 // Skip if we don't need to update the thumbnail. | 217 // Skip if we don't need to update the thumbnail. |
| 194 if (thumbnail_service.get() == NULL || | 218 if (thumbnail_service.get() == NULL || |
| 195 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { | 219 !thumbnail_service->ShouldAcquirePageThumbnail(url)) { |
| 196 return; | 220 return; |
| 197 } | 221 } |
| 198 | 222 |
| 223 web_contents->IncrementCapturerCount(gfx::Size()); | |
| 224 // It turns out that the corresponding DecrementCapturerCount() can trigger a | |
|
Lei Zhang
2016/01/09 02:14:24
This comment is about IncrementCapturerCount() rig
shrike
2016/01/11 19:07:08
Yeah. This latest patchset was a checkpoint, not r
| |
| 225 // call to content::WebContentsImpl::WasHidden(), which eventually calls | |
| 226 // ThumbnailTabHelper::UpdateThumbnailIfNecessary(), starting the thumbnail | |
| 227 // generation process all over again. Break this cycle by noting that we're | |
| 228 // currently generating a thumbnail. | |
| 199 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( | 229 scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm( |
|
Lei Zhang
2016/01/09 02:14:25
This can now be done in AsyncProcessThumbnail().
shrike
2016/01/11 19:07:08
Acknowledged.
| |
| 200 thumbnail_service->GetThumbnailingAlgorithm()); | 230 thumbnail_service->GetThumbnailingAlgorithm()); |
| 201 | 231 |
| 202 scoped_refptr<ThumbnailingContext> context(new ThumbnailingContext( | 232 thumbnailing_context_ = new ThumbnailingContext( |
| 203 web_contents, thumbnail_service.get(), load_interrupted_)); | 233 web_contents, thumbnail_service.get(), load_interrupted_); |
| 204 AsyncProcessThumbnail(web_contents, context, algorithm); | 234 AsyncProcessThumbnail(algorithm); |
| 205 } | 235 } |
| 206 | 236 |
| 207 void ThumbnailTabHelper::RenderViewHostCreated( | 237 void ThumbnailTabHelper::RenderViewHostCreated( |
| 208 content::RenderViewHost* renderer) { | 238 content::RenderViewHost* renderer) { |
| 209 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new | 239 // NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED is really a new |
| 210 // RenderView, not RenderViewHost, and there is no good way to get | 240 // RenderView, not RenderViewHost, and there is no good way to get |
| 211 // notifications of RenderViewHosts. So just be tolerant of re-registrations. | 241 // notifications of RenderViewHosts. So just be tolerant of re-registrations. |
| 212 bool registered = registrar_.IsRegistered( | 242 bool registered = registrar_.IsRegistered( |
| 213 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 243 this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
| 214 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 244 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
| 215 if (!registered) { | 245 if (!registered) { |
| 216 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 246 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
| 217 content::Source<RenderWidgetHost>(renderer->GetWidget())); | 247 content::Source<RenderWidgetHost>(renderer->GetWidget())); |
| 218 } | 248 } |
| 219 } | 249 } |
| 220 | 250 |
| 221 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { | 251 void ThumbnailTabHelper::WidgetHidden(RenderWidgetHost* widget) { |
| 222 UpdateThumbnailIfNecessary(web_contents()); | 252 UpdateThumbnailIfNecessary(web_contents()); |
| 223 } | 253 } |
| OLD | NEW |