Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Side by Side Diff: chrome/browser/tab_contents/thumbnail_generator.cc

Issue 661237: This adds in the ability for Chrome to generate windows with snapshots of all... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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/tab_contents/thumbnail_generator.h" 5 #include "chrome/browser/tab_contents/thumbnail_generator.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map>
8 9
9 #include "base/histogram.h" 10 #include "base/histogram.h"
10 #include "base/time.h" 11 #include "base/time.h"
11 #include "build/build_config.h" 12 #include "build/build_config.h"
12 #include "chrome/browser/renderer_host/backing_store.h" 13 #include "chrome/browser/renderer_host/backing_store.h"
13 #include "chrome/browser/renderer_host/render_view_host.h" 14 #include "chrome/browser/renderer_host/render_view_host.h"
15 #include "chrome/browser/tab_contents/tab_contents.h"
14 #include "chrome/common/notification_service.h" 16 #include "chrome/common/notification_service.h"
15 #include "chrome/common/property_bag.h" 17 #include "chrome/common/property_bag.h"
16 #include "gfx/rect.h" 18 #include "gfx/rect.h"
17 #include "gfx/skbitmap_operations.h" 19 #include "gfx/skbitmap_operations.h"
18 #include "skia/ext/platform_canvas.h" 20 #include "skia/ext/platform_canvas.h"
19 #include "third_party/skia/include/core/SkBitmap.h" 21 #include "third_party/skia/include/core/SkBitmap.h"
20 22
21 // Overview 23 // Overview
22 // -------- 24 // --------
23 // This class provides current thumbnails for tabs. The simplest operation is 25 // This class provides current thumbnails for tabs. The simplest operation is
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 100
99 // Get the bitmap as a Skia object so we can resample it. This is a large 101 // Get the bitmap as a Skia object so we can resample it. This is a large
100 // allocation and we can tolerate failure here, so give up if the allocation 102 // allocation and we can tolerate failure here, so give up if the allocation
101 // fails. 103 // fails.
102 skia::PlatformCanvas temp_canvas; 104 skia::PlatformCanvas temp_canvas;
103 if (!backing_store->CopyFromBackingStore(gfx::Rect(gfx::Point(0, 0), 105 if (!backing_store->CopyFromBackingStore(gfx::Rect(gfx::Point(0, 0),
104 backing_store->size()), 106 backing_store->size()),
105 &temp_canvas)) 107 &temp_canvas))
106 return result; 108 return result;
107 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); 109 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false);
108 result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp, kThumbnailWidth, 110
111 #if defined(OS_CHROMEOS)
112 // On ChromeOS, the thumbnail is always half the dimensions of the
113 // original.
114 result = SkBitmapOperations::DownsampleByTwo(bmp);
115 #else
116 result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp,
117 kThumbnailWidth,
109 kThumbnailHeight); 118 kThumbnailHeight);
110 119
111 // This is a bit subtle. SkBitmaps are refcounted, but the magic ones in 120 // This is a bit subtle. SkBitmaps are refcounted, but the magic
112 // PlatformCanvas can't be ssigned to SkBitmap with proper 121 // ones in PlatformCanvas can't be assigned to SkBitmap with proper
113 // refcounting. If the bitmap doesn't change, then the downsampler will 122 // refcounting. If the bitmap doesn't change, then the downsampler
114 // return the input bitmap, which will be the reference to the weird 123 // will return the input bitmap, which will be the reference to the
115 // PlatformCanvas one insetad of a regular one. To get a regular refcounted 124 // weird PlatformCanvas one insetad of a regular one. To get a
116 // bitmap, we need to copy it. 125 // regular refcounted bitmap, we need to copy it.
117 if (bmp.width() == result.width() && bmp.height() == result.height()) 126 if (bmp.width() == result.width() &&
127 bmp.height() == result.height())
118 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); 128 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
129 #endif
119 130
120 HISTOGRAM_TIMES(kThumbnailHistogramName, 131 HISTOGRAM_TIMES(kThumbnailHistogramName,
121 base::TimeTicks::Now() - begin_compute_thumbnail); 132 base::TimeTicks::Now() - begin_compute_thumbnail);
122 return result; 133 return result;
123 } 134 }
124 135
125 } // namespace 136 } // namespace
126 137
138 struct ThumbnailGenerator::AsyncRequestInfo {
139 scoped_ptr<ThumbnailReadyCallback> callback;
140 scoped_ptr<TransportDIB> thumbnail_dib;
141 RenderWidgetHost* renderer; // Not owned.
142 };
143
127 ThumbnailGenerator::ThumbnailGenerator() 144 ThumbnailGenerator::ThumbnailGenerator()
128 : no_timeout_(false) { 145 : no_timeout_(false) {
129 // The BrowserProcessImpl creates this non-lazily. If you add nontrivial 146 // The BrowserProcessImpl creates this non-lazily. If you add nontrivial
130 // stuff here, be sure to convert it to being lazily created. 147 // stuff here, be sure to convert it to being lazily created.
131 // 148 //
132 // We don't register for notifications here since BrowserProcessImpl creates 149 // We don't register for notifications here since BrowserProcessImpl creates
133 // us before the NotificationService is. 150 // us before the NotificationService is.
134 } 151 }
135 152
136 ThumbnailGenerator::~ThumbnailGenerator() { 153 ThumbnailGenerator::~ThumbnailGenerator() {
137 } 154 }
138 155
139 void ThumbnailGenerator::StartThumbnailing() { 156 void ThumbnailGenerator::StartThumbnailing() {
140 if (registrar_.IsEmpty()) { 157 if (registrar_.IsEmpty()) {
141 // Even though we deal in RenderWidgetHosts, we only care about its 158 // Even though we deal in RenderWidgetHosts, we only care about its
142 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails 159 // subclass, RenderViewHost when it is in a tab. We don't make thumbnails
143 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that 160 // for RenderViewHosts that aren't in tabs, or RenderWidgetHosts that
144 // aren't views like select popups. 161 // aren't views like select popups.
145 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB, 162 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
146 NotificationService::AllSources()); 163 NotificationService::AllSources());
147
148 registrar_.Add(this, NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED, 164 registrar_.Add(this, NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED,
149 NotificationService::AllSources()); 165 NotificationService::AllSources());
150 registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED, 166 registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,
151 NotificationService::AllSources()); 167 NotificationService::AllSources());
168 registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED,
169 NotificationService::AllSources());
152 } 170 }
153 } 171 }
154 172
173 void ThumbnailGenerator::AskForThumbnail(RenderWidgetHost* renderer,
174 ThumbnailReadyCallback* callback,
175 gfx::Size size) {
176 SkBitmap first_try = GetThumbnailForRenderer(renderer);
177 if (!first_try.isNull()) {
178 // We were able to find a non-null thumbnail for this renderer, so
179 // we'll go with it.
180 callback->Run(first_try);
181 delete callback;
182 return;
183 }
184
185 // We are going to render the thumbnail asynchronously now, so keep
186 // this callback for later lookup when the rendering is done.
187 static int sequence_num = 0;
188 TransportDIB* thumbnail_dib = TransportDIB::Create(
189 size.width() * size.height() * 4, sequence_num++);
190 linked_ptr<AsyncRequestInfo> request_info(new AsyncRequestInfo);
191 request_info->callback.reset(callback);
192 request_info->thumbnail_dib.reset(thumbnail_dib);
193 request_info->renderer = renderer;
194 ThumbnailCallbackMap::value_type new_value(thumbnail_dib->handle(),
195 request_info);
196 std::pair<ThumbnailCallbackMap::iterator, bool> result =
197 callback_map_.insert(new_value);
198 if (!result.second) {
199 NOTREACHED() << "Callback already registered?";
200 return;
201 }
202
203 renderer->PaintAtSize(thumbnail_dib->handle(), size);
204 }
205
155 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( 206 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
156 RenderWidgetHost* renderer) const { 207 RenderWidgetHost* renderer) const {
157 WidgetThumbnail* wt = GetDataForHost(renderer); 208 WidgetThumbnail* wt = GetDataForHost(renderer);
158 209
159 BackingStore* backing_store = renderer->GetBackingStore(false); 210 BackingStore* backing_store = renderer->GetBackingStore(false);
160 if (!backing_store) { 211 if (!backing_store) {
161 // When we have no backing store, there's no choice in what to use. We 212 // When we have no backing store, there's no choice in what to use. We
162 // have to return either the existing thumbnail or the empty one if there 213 // have to return either the existing thumbnail or the empty one if there
163 // isn't a saved one. 214 // isn't a saved one.
164 return wt->thumbnail; 215 return wt->thumbnail;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 base::TimeTicks::Now() - 265 base::TimeTicks::Now() -
215 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown) 266 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown)
216 return; // TODO(brettw) schedule thumbnail generation for this renderer in 267 return; // TODO(brettw) schedule thumbnail generation for this renderer in
217 // case we don't get a paint for it after the time slop, but it's 268 // case we don't get a paint for it after the time slop, but it's
218 // still visible. 269 // still visible.
219 270
220 // Clear the thumbnail, since it's now out of date. 271 // Clear the thumbnail, since it's now out of date.
221 wt->thumbnail = SkBitmap(); 272 wt->thumbnail = SkBitmap();
222 } 273 }
223 274
275 void ThumbnailGenerator::WidgetDidReceivePaintAtSizeAck(
276 RenderWidgetHost* widget,
277 const TransportDIB::Handle& dib_handle,
278 const gfx::Size& size) {
279 // Lookup the callback, run it, and erase it.
280 ThumbnailCallbackMap::iterator item = callback_map_.find(dib_handle);
281 if (item != callback_map_.end()) {
282 TransportDIB* dib = item->second->thumbnail_dib.get();
283 DCHECK(dib);
284 if (!dib) {
285 return;
286 }
287
288 // Create an SkBitmap from the DIB.
289 SkBitmap non_owned_bitmap;
290 SkBitmap result;
291
292 // Fill out the non_owned_bitmap with the right config. Note that
293 // this code assumes that the transport dib is a 32-bit ARGB
294 // image.
295 non_owned_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
296 size.width(), size.height());
297 non_owned_bitmap.setPixels(dib->memory());
298
299 // Now alloc/copy the memory so we own it and can pass it around,
300 // and the memory won't go away when the DIB goes away.
301 // TODO: Figure out a way to avoid this copy?
302 non_owned_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
303
304 item->second->callback->Run(result);
305
306 // We're done with the callback, and with the DIB, so delete both.
307 callback_map_.erase(item);
308 }
309 }
310
224 void ThumbnailGenerator::Observe(NotificationType type, 311 void ThumbnailGenerator::Observe(NotificationType type,
225 const NotificationSource& source, 312 const NotificationSource& source,
226 const NotificationDetails& details) { 313 const NotificationDetails& details) {
227 switch (type.value) { 314 switch (type.value) {
228 case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: { 315 case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: {
229 // Install our observer for all new RVHs. 316 // Install our observer for all new RVHs.
230 RenderViewHost* renderer = Details<RenderViewHost>(details).ptr(); 317 RenderViewHost* renderer = Details<RenderViewHost>(details).ptr();
231 renderer->set_painting_observer(this); 318 renderer->set_painting_observer(this);
232 break; 319 break;
233 } 320 }
234 321
235 case NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED: 322 case NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED:
236 if (*Details<bool>(details).ptr()) 323 if (*Details<bool>(details).ptr())
237 WidgetShown(Source<RenderWidgetHost>(source).ptr()); 324 WidgetShown(Source<RenderWidgetHost>(source).ptr());
238 else 325 else
239 WidgetHidden(Source<RenderWidgetHost>(source).ptr()); 326 WidgetHidden(Source<RenderWidgetHost>(source).ptr());
240 break; 327 break;
241 328
242 case NotificationType::RENDER_WIDGET_HOST_DESTROYED: 329 case NotificationType::RENDER_WIDGET_HOST_DESTROYED:
243 WidgetDestroyed(Source<RenderWidgetHost>(source).ptr()); 330 WidgetDestroyed(Source<RenderWidgetHost>(source).ptr());
244 break; 331 break;
245 332
333 case NotificationType::TAB_CONTENTS_DISCONNECTED:
334 TabContentsDisconnected(Source<TabContents>(source).ptr());
335 break;
336
246 default: 337 default:
247 NOTREACHED(); 338 NOTREACHED();
248 } 339 }
249 } 340 }
250 341
251 void ThumbnailGenerator::WidgetShown(RenderWidgetHost* widget) { 342 void ThumbnailGenerator::WidgetShown(RenderWidgetHost* widget) {
252 WidgetThumbnail* wt = GetDataForHost(widget); 343 WidgetThumbnail* wt = GetDataForHost(widget);
253 wt->last_shown = base::TimeTicks::Now(); 344 wt->last_shown = base::TimeTicks::Now();
254 345
255 // If there is no thumbnail (like we're displaying a background tab for the 346 // If there is no thumbnail (like we're displaying a background tab for the
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 // make a new one. 378 // make a new one.
288 if (!wt->thumbnail.isNull()) 379 if (!wt->thumbnail.isNull())
289 return; 380 return;
290 wt->thumbnail = GetThumbnailForRenderer(widget); 381 wt->thumbnail = GetThumbnailForRenderer(widget);
291 } 382 }
292 383
293 void ThumbnailGenerator::WidgetDestroyed(RenderWidgetHost* widget) { 384 void ThumbnailGenerator::WidgetDestroyed(RenderWidgetHost* widget) {
294 EraseHostFromShownList(widget); 385 EraseHostFromShownList(widget);
295 } 386 }
296 387
388 void ThumbnailGenerator::TabContentsDisconnected(TabContents* contents) {
389 // Go through the existing callbacks, and find any that have the
390 // same renderer as this TabContents and remove them so they don't
391 // hang around.
392 ThumbnailCallbackMap::iterator iterator = callback_map_.begin();
393 RenderWidgetHost* renderer = contents->render_view_host();
394 while (iterator != callback_map_.end()) {
395 if (iterator->second->renderer == renderer) {
396 callback_map_.erase(iterator);
397 }
398 ++iterator;
399 }
400 }
401
297 void ThumbnailGenerator::ShownDelayHandler() { 402 void ThumbnailGenerator::ShownDelayHandler() {
298 base::TimeTicks threshold = base::TimeTicks::Now() - 403 base::TimeTicks threshold = base::TimeTicks::Now() -
299 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS); 404 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS);
300 405
301 // Check the list of all pending RWHs (normally only one) to see if any of 406 // Check the list of all pending RWHs (normally only one) to see if any of
302 // their times have expired. 407 // their times have expired.
303 for (size_t i = 0; i < shown_hosts_.size(); i++) { 408 for (size_t i = 0; i < shown_hosts_.size(); i++) {
304 WidgetThumbnail* wt = GetDataForHost(shown_hosts_[i]); 409 WidgetThumbnail* wt = GetDataForHost(shown_hosts_[i]);
305 if (no_timeout_ || wt->last_shown <= threshold) { 410 if (no_timeout_ || wt->last_shown <= threshold) {
306 // This thumbnail has expired, delete it. 411 // This thumbnail has expired, delete it.
(...skipping 12 matching lines...) Expand all
319 &ThumbnailGenerator::ShownDelayHandler); 424 &ThumbnailGenerator::ShownDelayHandler);
320 } 425 }
321 } 426 }
322 427
323 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { 428 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) {
324 std::vector<RenderWidgetHost*>::iterator found = 429 std::vector<RenderWidgetHost*>::iterator found =
325 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget); 430 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget);
326 if (found != shown_hosts_.end()) 431 if (found != shown_hosts_.end())
327 shown_hosts_.erase(found); 432 shown_hosts_.erase(found);
328 } 433 }
OLDNEW
« no previous file with comments | « chrome/browser/tab_contents/thumbnail_generator.h ('k') | chrome/browser/tab_contents/thumbnail_generator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698