| OLD | NEW | 
|---|
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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 } | 
| OLD | NEW | 
|---|