OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/android/thumbnail_cache.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <cmath> |
| 9 |
| 10 #include "base/file_util.h" |
| 11 #include "base/files/file.h" |
| 12 #include "base/files/file_enumerator.h" |
| 13 #include "base/files/file_path.h" |
| 14 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/time/time.h" |
| 16 #include "chrome/browser/android/tab_thumbnail_provider.h" |
| 17 #include "content/public/browser/android/content_view_core.h" |
| 18 #include "content/public/browser/android/ui_resource_provider.h" |
| 19 #include "content/public/browser/browser_thread.h" |
| 20 #include "content/public/browser/render_view_host.h" |
| 21 #include "content/public/browser/render_widget_host_view.h" |
| 22 #include "content/public/browser/web_contents.h" |
| 23 #include "skia/ext/refptr.h" |
| 24 #include "third_party/android_opengl/etc1/etc1.h" |
| 25 #include "third_party/skia/include/core/SkBitmap.h" |
| 26 #include "third_party/skia/include/core/SkCanvas.h" |
| 27 #include "third_party/skia/include/core/SkData.h" |
| 28 #include "third_party/skia/include/core/SkMallocPixelRef.h" |
| 29 #include "third_party/skia/include/core/SkPixelRef.h" |
| 30 #include "ui/gfx/geometry/size_conversions.h" |
| 31 |
| 32 namespace { |
| 33 |
| 34 const size_t kMaxReadbacks = 1; |
| 35 const bool kDropCachedNTPOnLowMemory = true; |
| 36 const float kApproximationScaleFactor = 4.f; |
| 37 const bool kEnableCompression = true; |
| 38 const base::TimeDelta kCaptureMinRequestTimeMs( |
| 39 base::TimeDelta::FromMilliseconds(1000)); |
| 40 const int kCompressedKey = 0xABABABAB; |
| 41 const int kDecompressedKey = 0xCDCDCDCD; |
| 42 |
| 43 // ETC1 texture sizes are multiples of four. |
| 44 size_t NextETC1Size(size_t s) { |
| 45 return (s / 4 + (s % 4 ? 1 : 0)) * 4; |
| 46 } |
| 47 |
| 48 gfx::Size GetEncodedSize(gfx::Size bitmap_size) { |
| 49 return gfx::Size(NextETC1Size(bitmap_size.width()), |
| 50 NextETC1Size(bitmap_size.height())); |
| 51 } |
| 52 |
| 53 } // anonymous namespace |
| 54 |
| 55 ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str, |
| 56 size_t default_cache_size, |
| 57 size_t approximation_cache_size, |
| 58 size_t compression_queue_max_size, |
| 59 size_t write_queue_max_size, |
| 60 bool use_approximation_thumbnails, |
| 61 float thumbnail_scale) |
| 62 : ui_resource_provider_(NULL), |
| 63 disk_cache_path_(disk_cache_path_str), |
| 64 compression_queue_max_size_(compression_queue_max_size), |
| 65 write_queue_max_size_(write_queue_max_size), |
| 66 cache_(default_cache_size), |
| 67 approximation_cache_(approximation_cache_size), |
| 68 use_approximation_thumbnails_(use_approximation_thumbnails), |
| 69 thumbnail_scale_(thumbnail_scale), |
| 70 compression_thread_("thumbnail_compression"), |
| 71 weak_factory_(this) { |
| 72 compression_thread_.Start(); |
| 73 } |
| 74 |
| 75 ThumbnailCache::~ThumbnailCache() { |
| 76 compression_thread_.Stop(); |
| 77 SetUIResourceProvider(NULL); |
| 78 } |
| 79 |
| 80 void ThumbnailCache::SetUIResourceProvider( |
| 81 content::UIResourceProvider* ui_resource_provider) { |
| 82 if (ui_resource_provider_ == ui_resource_provider) |
| 83 return; |
| 84 |
| 85 // Clean up the UI resources. |
| 86 for (ExpiringThumbnailCache::iterator iter = approximation_cache_.begin(); |
| 87 iter != approximation_cache_.end(); |
| 88 iter++) { |
| 89 scoped_refptr<Thumbnail> thumbnail = iter->second; |
| 90 thumbnail->CleanupThumbnail(); |
| 91 } |
| 92 |
| 93 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); |
| 94 iter != cache_.end(); |
| 95 iter++) { |
| 96 scoped_refptr<Thumbnail> thumbnail = iter->second; |
| 97 thumbnail->CleanupThumbnail(); |
| 98 } |
| 99 |
| 100 if (ui_resource_provider_) |
| 101 ui_resource_provider_->RemoveListener(this); |
| 102 |
| 103 ui_resource_provider_ = ui_resource_provider; |
| 104 |
| 105 if (ui_resource_provider_) |
| 106 ui_resource_provider_->AddListener(this); |
| 107 } |
| 108 |
| 109 void ThumbnailCache::CacheInTab(TabId tab_id) { |
| 110 GetThumbnail(tab_id, true); |
| 111 } |
| 112 |
| 113 void ThumbnailCache::AddThumbnailChangeListener( |
| 114 ThumbnailChangeListener* listener) { |
| 115 if (thumbnail_change_listeners_.find(listener) == |
| 116 thumbnail_change_listeners_.end()) { |
| 117 thumbnail_change_listeners_.insert(listener); |
| 118 } |
| 119 } |
| 120 |
| 121 void ThumbnailCache::RemoveThumbnailChangeListener( |
| 122 ThumbnailChangeListener* listener) { |
| 123 ThumbnailChangeListenerSet::iterator iter = |
| 124 thumbnail_change_listeners_.find(listener); |
| 125 if (iter != thumbnail_change_listeners_.end()) |
| 126 thumbnail_change_listeners_.erase(iter); |
| 127 } |
| 128 |
| 129 scoped_refptr<ThumbnailCache::Thumbnail> ThumbnailCache::GetThumbnail( |
| 130 TabId id) { |
| 131 return GetThumbnail(id, false); |
| 132 } |
| 133 |
| 134 scoped_refptr<ThumbnailCache::Thumbnail> ThumbnailCache::GetThumbnail( |
| 135 TabId id, |
| 136 bool cache_in_if_missing) { |
| 137 scoped_refptr<Thumbnail> thumbnail = cache_.Get(id); |
| 138 |
| 139 if (!thumbnail) { |
| 140 if (cache_in_if_missing) { |
| 141 AddIdToVisibleIds(id); |
| 142 ReadNextThumbnail(id); |
| 143 } |
| 144 thumbnail = approximation_cache_.Get(id); |
| 145 } |
| 146 |
| 147 return thumbnail; |
| 148 } |
| 149 |
| 150 bool ThumbnailCache::CanReadTabContent(const TabThumbnailProvider* tab) { |
| 151 content::ContentViewCore* view = tab->GetContentViewCore(); |
| 152 return view && |
| 153 view->GetWebContents()->GetRenderViewHost()->CanCopyFromBackingStore(); |
| 154 } |
| 155 |
| 156 void ThumbnailCache::CacheTabThumbnail(const TabThumbnailProvider* tab) { |
| 157 TabId tab_id = tab->GetAndroidId(); |
| 158 if (pending_thumbnail_readbacks_.find(tab_id) != |
| 159 pending_thumbnail_readbacks_.end() || |
| 160 pending_thumbnail_readbacks_.size() >= kMaxReadbacks) { |
| 161 return; |
| 162 } |
| 163 |
| 164 InvalidateIfChanged(tab); |
| 165 if (CheckAndUpdateThumbnailMetaData(tab, false)) { |
| 166 base::Callback<void(int, const ThumbnailBitmap&)> end_callback = base::Bind( |
| 167 &ThumbnailCache::EndCacheTabThumbnail, weak_factory_.GetWeakPtr()); |
| 168 scoped_refptr<TabReadbackRequest> readback = |
| 169 make_scoped_refptr(new TabReadbackRequest( |
| 170 tab_id, tab->GetContentViewCore(), thumbnail_scale_, end_callback)); |
| 171 pending_thumbnail_readbacks_[tab_id] = readback; |
| 172 readback->Run(); |
| 173 } |
| 174 } |
| 175 |
| 176 void ThumbnailCache::CacheTabThumbnailWithBitmap( |
| 177 const TabThumbnailProvider* tab, |
| 178 const SkBitmap& bitmap, |
| 179 float thumbnail_scale) { |
| 180 bool is_native_page = true; |
| 181 TabId tab_id = tab->GetAndroidId(); |
| 182 InvalidateIfChanged(tab); |
| 183 if (CheckAndUpdateThumbnailMetaData(tab, is_native_page)) |
| 184 PutThumbnail(tab_id, ThumbnailBitmap(bitmap, thumbnail_scale)); |
| 185 } |
| 186 |
| 187 void ThumbnailCache::EndCacheTabThumbnail(TabId tab_id, |
| 188 const ThumbnailBitmap& bitmap) { |
| 189 bool is_native_page = false; |
| 190 TabReadbackRequestMap::iterator readback_iter = |
| 191 pending_thumbnail_readbacks_.find(tab_id); |
| 192 if (readback_iter != pending_thumbnail_readbacks_.end()) { |
| 193 scoped_refptr<TabReadbackRequest> readback = readback_iter->second; |
| 194 pending_thumbnail_readbacks_.erase(tab_id); |
| 195 if (!readback->drop_after_readback() && bitmap.IsValid()) { |
| 196 PutThumbnail(tab_id, bitmap); |
| 197 } |
| 198 NotifyListenersOfThumbnailChange(tab_id, is_native_page); |
| 199 } |
| 200 } |
| 201 |
| 202 void ThumbnailCache::InvalidateIfChanged(const TabThumbnailProvider* tab) { |
| 203 TabId tab_id = tab->GetAndroidId(); |
| 204 ThumbnailMetaDataMap::iterator meta_data_iter = |
| 205 thumbnail_meta_data_.find(tab_id); |
| 206 if (meta_data_iter == thumbnail_meta_data_.end()) { |
| 207 // We need this case so we have metadata when loading the thumbnail from |
| 208 // disk. Note that the time stamp is 0 to force invalidation when a new |
| 209 // thumbnail is requested to be captured. |
| 210 thumbnail_meta_data_[tab_id] = |
| 211 ThumbnailMetaData(base::Time(), tab->GetURL()); |
| 212 } else if (meta_data_iter->second.url() != tab->GetURL()) { |
| 213 Remove(tab_id); |
| 214 } |
| 215 } |
| 216 |
| 217 void ThumbnailCache::PutThumbnail(TabId tab_id, |
| 218 const ThumbnailBitmap& thumbnail_bitmap) { |
| 219 if (!ui_resource_provider_ || !thumbnail_bitmap.IsValid()) |
| 220 return; |
| 221 |
| 222 scoped_refptr<Thumbnail> thumbnail = |
| 223 make_scoped_refptr(new Thumbnail(tab_id, |
| 224 thumbnail_bitmap.bitmap(), |
| 225 thumbnail_bitmap.scale(), |
| 226 ui_resource_provider_)); |
| 227 PutThumbnail(tab_id, thumbnail_bitmap, thumbnail); |
| 228 } |
| 229 |
| 230 void ThumbnailCache::PutThumbnail(TabId tab_id, |
| 231 const ThumbnailBitmap& thumbnail_bitmap, |
| 232 scoped_refptr<Thumbnail> thumbnail) { |
| 233 if (!ui_resource_provider_ || !thumbnail || !thumbnail_bitmap.IsValid()) |
| 234 return; |
| 235 |
| 236 scoped_refptr<Thumbnail> approx_thumbnail = NULL; |
| 237 ThumbnailBitmap approx_thumbnail_bitmap; |
| 238 if (use_approximation_thumbnails_) |
| 239 approx_thumbnail_bitmap = thumbnail_bitmap.CreateApproximation(); |
| 240 |
| 241 if (approx_thumbnail_bitmap.IsValid()) { |
| 242 approx_thumbnail = |
| 243 make_scoped_refptr(new ApproxThumbnail(tab_id, |
| 244 approx_thumbnail_bitmap.bitmap(), |
| 245 approx_thumbnail_bitmap.scale(), |
| 246 ui_resource_provider_)); |
| 247 } |
| 248 |
| 249 AddIdToVisibleIds(tab_id); |
| 250 RemoveFromQueues(tab_id); |
| 251 MakeSpaceForNewItemIfNecessary(tab_id); |
| 252 CleanupThumbnail(cache_.Get(tab_id)); |
| 253 cache_.Put(tab_id, thumbnail); |
| 254 |
| 255 if (approx_thumbnail) { |
| 256 CleanupThumbnail(approximation_cache_.Get(tab_id)); |
| 257 approximation_cache_.Put(tab_id, approx_thumbnail); |
| 258 } |
| 259 |
| 260 CompressThumbnailIfNecessary(thumbnail); |
| 261 WriteThumbnailIfNecessary(thumbnail); |
| 262 NotifyListenersOfThumbnailChange(tab_id, true); |
| 263 } |
| 264 |
| 265 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData( |
| 266 const TabThumbnailProvider* tab, |
| 267 bool is_native_page) { |
| 268 if (!is_native_page && !CanReadTabContent(tab)) |
| 269 return false; |
| 270 |
| 271 // TODO(tedchoc): Add check to see if the tab has actually drawn content. |
| 272 // Then add a draw counter to see if the content has actually changed since |
| 273 // last thumbnail capture. |
| 274 TabId tab_id = tab->GetAndroidId(); |
| 275 ThumbnailMetaDataMap::iterator meta_data_iter = |
| 276 thumbnail_meta_data_.find(tab_id); |
| 277 if (meta_data_iter != thumbnail_meta_data_.end() && |
| 278 meta_data_iter->second.url() == tab->GetURL() && |
| 279 (CurrentTime() - meta_data_iter->second.capture_time()) < |
| 280 kCaptureMinRequestTimeMs) { |
| 281 return false; |
| 282 } |
| 283 |
| 284 thumbnail_meta_data_[tab_id] = |
| 285 ThumbnailMetaData(CurrentTime(), tab->GetURL()); |
| 286 return true; |
| 287 } |
| 288 |
| 289 void ThumbnailCache::RemoveFromCache(ExpiringThumbnailCache& cache, |
| 290 const TabIdList& tab_ids) { |
| 291 for (TabIdList::const_iterator iter = tab_ids.begin(); iter != tab_ids.end(); |
| 292 iter++) { |
| 293 cache.Remove(*iter); |
| 294 } |
| 295 } |
| 296 |
| 297 void ThumbnailCache::RemoveThumbnailsAndScheduleReload() { |
| 298 TabIdList modified_entries; |
| 299 TabIdList remove_from_cache; |
| 300 for (ExpiringThumbnailCache::iterator iter = approximation_cache_.begin(); |
| 301 iter != approximation_cache_.end(); |
| 302 iter++) { |
| 303 TabId tab_id = iter->first; |
| 304 scoped_refptr<Thumbnail> thumbnail = iter->second; |
| 305 thumbnail->CleanupThumbnail(); |
| 306 |
| 307 if (!thumbnail->AttemptToScheduleRebuildFromData()) |
| 308 remove_from_cache.push_back(tab_id); |
| 309 modified_entries.push_back(tab_id); |
| 310 } |
| 311 |
| 312 read_queue_.clear(); |
| 313 RemoveFromCache(approximation_cache_, remove_from_cache); |
| 314 remove_from_cache.clear(); |
| 315 |
| 316 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); |
| 317 iter != cache_.end(); |
| 318 iter++) { |
| 319 TabId tab_id = iter->first; |
| 320 scoped_refptr<Thumbnail> thumbnail = iter->second; |
| 321 thumbnail->CleanupThumbnail(); |
| 322 |
| 323 if (!thumbnail->AttemptToScheduleRebuildFromData()) |
| 324 remove_from_cache.push_back(tab_id); |
| 325 modified_entries.push_back(tab_id); |
| 326 } |
| 327 |
| 328 RemoveFromCache(cache_, remove_from_cache); |
| 329 remove_from_cache.clear(); |
| 330 |
| 331 for (TabIdList::iterator iter = modified_entries.begin(); |
| 332 iter != modified_entries.end(); |
| 333 iter++) { |
| 334 NotifyListenersOfThumbnailChange(*iter, false); |
| 335 } |
| 336 |
| 337 ReadNextThumbnail(); |
| 338 |
| 339 TabIdList last_visible_ids = last_visible_ids_; |
| 340 last_visible_ids_.clear(); |
| 341 UpdateVisibleIds(last_visible_ids); |
| 342 } |
| 343 |
| 344 void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) { |
| 345 if (priority.empty()) { |
| 346 last_visible_ids_.clear(); |
| 347 return; |
| 348 } |
| 349 |
| 350 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize()); |
| 351 if (last_visible_ids_.size() == ids_size) { |
| 352 // Early out if called with the same input as last time (We only care |
| 353 // about the first mCache.MaximumCacheSize() entries). |
| 354 bool lists_differ = false; |
| 355 for (TabIdList::const_iterator visible_iter = last_visible_ids_.begin(), |
| 356 priority_iter = priority.begin(); |
| 357 visible_iter != last_visible_ids_.end() && |
| 358 priority_iter != priority.end(); |
| 359 visible_iter++, priority_iter++) { |
| 360 if (*priority_iter != *visible_iter) { |
| 361 lists_differ = true; |
| 362 break; |
| 363 } |
| 364 } |
| 365 if (!lists_differ) |
| 366 return; |
| 367 } |
| 368 |
| 369 last_visible_ids_.clear(); |
| 370 visible_ids_.clear(); |
| 371 size_t count = 0; |
| 372 for (TabIdList::const_iterator iter = priority.begin(); |
| 373 iter != priority.end() && count < ids_size; |
| 374 iter++, count++) { |
| 375 TabId tab_id = *iter; |
| 376 last_visible_ids_.push_back(tab_id); |
| 377 visible_ids_.push_back(tab_id); |
| 378 } |
| 379 |
| 380 read_queue_.clear(); |
| 381 |
| 382 count = 0; |
| 383 for (TabIdList::const_iterator iter = priority.begin(); |
| 384 iter != priority.end() && count < ids_size; |
| 385 iter++, count++) { |
| 386 TabId tab_id = *iter; |
| 387 if (!cache_.Get(tab_id) && |
| 388 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == |
| 389 read_queue_.end()) { |
| 390 read_queue_.push_back(tab_id); |
| 391 } |
| 392 } |
| 393 |
| 394 ReadNextThumbnail(); |
| 395 } |
| 396 |
| 397 void ThumbnailCache::Remove(TabId tab_id) { |
| 398 TabReadbackRequestMap::iterator readback_iter = |
| 399 pending_thumbnail_readbacks_.find(tab_id); |
| 400 if (readback_iter != pending_thumbnail_readbacks_.end()) |
| 401 readback_iter->second->SetDropAfterReadback(true); |
| 402 |
| 403 scoped_refptr<Thumbnail> thumbnail = cache_.Remove(tab_id); |
| 404 CleanupThumbnail(thumbnail); |
| 405 |
| 406 scoped_refptr<Thumbnail> approx_thumbnail = |
| 407 approximation_cache_.Remove(tab_id); |
| 408 CleanupThumbnail(approx_thumbnail); |
| 409 |
| 410 thumbnail_meta_data_.erase(tab_id); |
| 411 RemoveFromDisk(tab_id); |
| 412 RemoveFromQueues(tab_id); |
| 413 |
| 414 if (thumbnail || approx_thumbnail) |
| 415 NotifyListenersOfThumbnailChange(tab_id, false); |
| 416 } |
| 417 |
| 418 void ThumbnailCache::HandleLowMemory(bool consider_gpu_memory) { |
| 419 HandleLowMemoryOnQueue(compression_queue_, consider_gpu_memory); |
| 420 HandleLowMemoryOnQueue(write_queue_, consider_gpu_memory); |
| 421 |
| 422 read_queue_.clear(); |
| 423 |
| 424 size_t cached_tabs_free_count = 0; |
| 425 if (cache_.size() > 0) { |
| 426 cached_tabs_free_count = std::max(cache_.size() / 2, 1u); |
| 427 for (size_t i = 0; i < cached_tabs_free_count; ++i) { |
| 428 RemoveThumbnailFromCache(); |
| 429 } |
| 430 } |
| 431 } |
| 432 |
| 433 size_t ThumbnailCache::HandleLowMemoryOnQueue(ThumbnailQueue& queue, |
| 434 bool consider_gpu_memory) { |
| 435 size_t thumbnails_dropped = 0; |
| 436 ThumbnailQueue next_queue; |
| 437 for (ThumbnailQueue::iterator iter = queue.begin(); iter != queue.end(); |
| 438 iter++) { |
| 439 scoped_refptr<Thumbnail> thumbnail = *iter; |
| 440 TabId tab_id = thumbnail->tab_id(); |
| 441 if (!consider_gpu_memory && thumbnail.get() == cache_.Get(tab_id).get()) |
| 442 RebuildThumbnail(thumbnail, true); |
| 443 |
| 444 CleanupThumbnail(thumbnail); |
| 445 ++thumbnails_dropped; |
| 446 } |
| 447 |
| 448 queue = next_queue; |
| 449 |
| 450 return thumbnails_dropped; |
| 451 } |
| 452 |
| 453 void ThumbnailCache::RemoveFromQueues(TabId tab_id) { |
| 454 TabIdList::iterator read_iter = |
| 455 std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
| 456 if (read_iter != read_queue_.end()) |
| 457 read_queue_.erase(read_iter); |
| 458 |
| 459 ThumbnailQueue new_write_queue; |
| 460 for (ThumbnailQueue::iterator iter = write_queue_.begin(); |
| 461 iter != write_queue_.end(); |
| 462 iter++) { |
| 463 scoped_refptr<Thumbnail> write_thumbnail = *iter; |
| 464 if (write_thumbnail->tab_id() == tab_id) { |
| 465 write_thumbnail->CleanupCPUData(true); |
| 466 CleanupThumbnail(write_thumbnail); |
| 467 } else { |
| 468 new_write_queue.push_back(write_thumbnail); |
| 469 } |
| 470 } |
| 471 write_queue_ = new_write_queue; |
| 472 |
| 473 ThumbnailQueue new_compression_queue; |
| 474 for (ThumbnailQueue::iterator iter = compression_queue_.begin(); |
| 475 iter != compression_queue_.end(); |
| 476 iter++) { |
| 477 scoped_refptr<Thumbnail> compress_thumbnail = *iter; |
| 478 if (compress_thumbnail->tab_id() == tab_id) { |
| 479 compress_thumbnail->CleanupCPUData(true); |
| 480 CleanupThumbnail(compress_thumbnail); |
| 481 } else { |
| 482 new_compression_queue.push_back(compress_thumbnail); |
| 483 } |
| 484 } |
| 485 compression_queue_ = new_compression_queue; |
| 486 } |
| 487 |
| 488 bool ThumbnailCache::WriteThumbnailIfNecessary( |
| 489 scoped_refptr<Thumbnail> thumbnail) { |
| 490 if (!thumbnail->DataWriteRequired()) |
| 491 return false; |
| 492 |
| 493 if (RemoveDuplicateIdsFromQueueHelper(write_queue_, thumbnail)) |
| 494 return true; |
| 495 |
| 496 if (write_queue_.size() > write_queue_max_size_) { |
| 497 CleanupThumbnail(write_queue_.front()); |
| 498 write_queue_.pop_front(); |
| 499 } |
| 500 |
| 501 write_queue_.push_back(thumbnail); |
| 502 WriteNextThumbnail(); |
| 503 return true; |
| 504 } |
| 505 |
| 506 bool ThumbnailCache::CompressThumbnailIfNecessary( |
| 507 scoped_refptr<Thumbnail> thumbnail) { |
| 508 if (!thumbnail->CompressionRequired()) |
| 509 return false; |
| 510 |
| 511 if (RemoveDuplicateIdsFromQueueHelper(compression_queue_, thumbnail)) |
| 512 return true; |
| 513 |
| 514 if (compression_queue_.size() > compression_queue_max_size_) { |
| 515 CleanupThumbnail(compression_queue_.front()); |
| 516 compression_queue_.pop_front(); |
| 517 } |
| 518 |
| 519 compression_queue_.push_back(thumbnail); |
| 520 CompressNextThumbnail(); |
| 521 return true; |
| 522 } |
| 523 |
| 524 bool ThumbnailCache::RemoveDuplicateIdsFromQueueHelper( |
| 525 ThumbnailQueue& queue, |
| 526 scoped_refptr<Thumbnail> thumbnail) { |
| 527 bool found = false; |
| 528 ThumbnailQueue remove_queue; |
| 529 for (ThumbnailQueue::iterator iter = queue.begin(); iter != queue.end(); |
| 530 iter++) { |
| 531 if (iter->get() == thumbnail.get()) |
| 532 found = true; |
| 533 if ((*iter)->tab_id() == thumbnail->tab_id()) { |
| 534 remove_queue.push_back(thumbnail); |
| 535 CleanupThumbnail(thumbnail); |
| 536 } |
| 537 } |
| 538 return found; |
| 539 } |
| 540 |
| 541 void ThumbnailCache::WriteNextThumbnail() { |
| 542 if (!write_queue_.empty()) { |
| 543 scoped_refptr<Thumbnail> thumbnail = write_queue_.front(); |
| 544 write_queue_.pop_front(); |
| 545 |
| 546 if (!thumbnail->IsValid()) |
| 547 return; |
| 548 |
| 549 // Make a copy of the thumbnail for write to disk. This copy increases the |
| 550 // ref-count of the pixels that the thumbnail is holding on to. We do this |
| 551 // to avoid concurrent access issues. |
| 552 scoped_refptr<Thumbnail> task_thumbnail = |
| 553 make_scoped_refptr(new Thumbnail(*thumbnail)); |
| 554 |
| 555 base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask, |
| 556 GetFilePath(task_thumbnail->tab_id()), |
| 557 task_thumbnail); |
| 558 base::Closure post_write_task = base::Bind(&ThumbnailCache::PostWriteTask, |
| 559 weak_factory_.GetWeakPtr(), |
| 560 task_thumbnail); |
| 561 content::BrowserThread::PostTaskAndReply( |
| 562 content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task); |
| 563 } |
| 564 } |
| 565 |
| 566 void ThumbnailCache::WriteTask(const base::FilePath& file_path, |
| 567 scoped_refptr<Thumbnail> thumbnail) { |
| 568 DCHECK(thumbnail); |
| 569 DCHECK(thumbnail->IsValid()); |
| 570 base::File file(file_path, |
| 571 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| 572 thumbnail->WriteThumbnailToFile(file); |
| 573 file.Close(); |
| 574 } |
| 575 |
| 576 void ThumbnailCache::PostWriteTask(scoped_refptr<Thumbnail> thumbnail) { |
| 577 DCHECK(thumbnail); |
| 578 // If the write has failed, then this thumbnail is invalidated, we delete the |
| 579 // associated file. The tab Id should still be valid. |
| 580 TabId tab_id = thumbnail->tab_id(); |
| 581 if (!thumbnail->IsValid()) |
| 582 RemoveCorruptThumbnailSource(tab_id); |
| 583 CleanupThumbnail(GetThumbnail(tab_id)); |
| 584 WriteNextThumbnail(); |
| 585 } |
| 586 |
| 587 void ThumbnailCache::ReadNextThumbnail(TabId id) { |
| 588 read_queue_.clear(); |
| 589 if (std::find(read_queue_.begin(), read_queue_.end(), id) == |
| 590 read_queue_.end()) |
| 591 read_queue_.push_back(id); |
| 592 ReadNextThumbnail(); |
| 593 } |
| 594 |
| 595 void ThumbnailCache::ReadNextThumbnail() { |
| 596 if (!read_queue_.empty()) { |
| 597 TabId tab_id = read_queue_.front(); |
| 598 // Create a thumbnail to hold the content of the read. |
| 599 scoped_refptr<Thumbnail> thumbnail = |
| 600 make_scoped_refptr(new Thumbnail(tab_id, ui_resource_provider_)); |
| 601 |
| 602 base::Closure read_task = base::Bind( |
| 603 &ThumbnailCache::ReadTask, GetFilePath(thumbnail->tab_id()), thumbnail); |
| 604 base::Closure post_read_task = base::Bind( |
| 605 &ThumbnailCache::PostReadTask, weak_factory_.GetWeakPtr(), thumbnail); |
| 606 content::BrowserThread::PostTaskAndReply( |
| 607 content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task); |
| 608 } |
| 609 } |
| 610 |
| 611 void ThumbnailCache::ReadTask(const base::FilePath& file_path, |
| 612 scoped_refptr<Thumbnail> thumbnail) { |
| 613 DCHECK(thumbnail); |
| 614 if (!base::PathExists(file_path)) |
| 615 return; |
| 616 |
| 617 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 618 DCHECK(file.IsValid()); |
| 619 thumbnail->ReadThumbnailFromFile(file); |
| 620 file.Close(); |
| 621 } |
| 622 |
| 623 void ThumbnailCache::PostReadTask(scoped_refptr<Thumbnail> thumbnail) { |
| 624 DCHECK(thumbnail); |
| 625 TabIdList::iterator iter = |
| 626 std::find(read_queue_.begin(), read_queue_.end(), thumbnail->tab_id()); |
| 627 if (iter == read_queue_.end()) |
| 628 return; |
| 629 |
| 630 read_queue_.erase(iter); |
| 631 |
| 632 // If the read has failed, then this thumbnail is invalidated, we delete the |
| 633 // associated file. The tab Id should still be valid. |
| 634 TabId tab_id = thumbnail->tab_id(); |
| 635 if (!thumbnail->IsValid()) { |
| 636 RemoveCorruptThumbnailSource(tab_id); |
| 637 ReadNextThumbnail(); |
| 638 return; |
| 639 } |
| 640 |
| 641 // TODO(powei): We should be able to remove this. All on-disk thumbnails |
| 642 // are compressed and so are not amenable to approximation. |
| 643 scoped_refptr<Thumbnail> approx_thumbnail; |
| 644 if (!approximation_cache_.Contains(tab_id)) { |
| 645 SkBitmap raw_data = thumbnail->raw_data(); |
| 646 if (!raw_data.empty() && use_approximation_thumbnails_) { |
| 647 ThumbnailBitmap thumbnail_bitmap(raw_data, thumbnail->scale()); |
| 648 ThumbnailBitmap approx_thumbnail_bitmap = |
| 649 thumbnail_bitmap.CreateApproximation(); |
| 650 if (approx_thumbnail_bitmap.IsValid()) { |
| 651 approx_thumbnail = make_scoped_refptr( |
| 652 new ApproxThumbnail(tab_id, |
| 653 approx_thumbnail_bitmap.bitmap(), |
| 654 approx_thumbnail_bitmap.scale(), |
| 655 ui_resource_provider_)); |
| 656 } |
| 657 } |
| 658 } |
| 659 MakeSpaceForNewItemIfNecessary(tab_id); |
| 660 scoped_refptr<Thumbnail> old_thumbnail = cache_.Remove(tab_id); |
| 661 CleanupThumbnail(old_thumbnail); |
| 662 cache_.Put(tab_id, thumbnail); |
| 663 if (approx_thumbnail) { |
| 664 scoped_refptr<Thumbnail> old_approx_thumbnail = |
| 665 approximation_cache_.Remove(tab_id); |
| 666 CleanupThumbnail(old_approx_thumbnail); |
| 667 approximation_cache_.Put(tab_id, approx_thumbnail); |
| 668 } |
| 669 |
| 670 RebuildThumbnail(thumbnail, false); |
| 671 NotifyListenersOfThumbnailChange(tab_id, true); |
| 672 |
| 673 // TODO(powei): We should be able to remove this. All on-disk thumbnails are |
| 674 // compressed. |
| 675 if (thumbnail->CompressionRequired()) { |
| 676 compression_queue_.push_back(thumbnail); |
| 677 CompressNextThumbnail(); |
| 678 } |
| 679 |
| 680 ReadNextThumbnail(); |
| 681 } |
| 682 |
| 683 void ThumbnailCache::CompressNextThumbnail() { |
| 684 if (!compression_queue_.empty()) { |
| 685 scoped_refptr<Thumbnail> thumbnail = compression_queue_.front(); |
| 686 compression_queue_.pop_front(); |
| 687 |
| 688 // Make a copy of the thumbnail for compression. This copy increases the |
| 689 // ref-count of the pixels that the thumbnail is holding on to. We do this |
| 690 // to avoid concurrent access issues. |
| 691 scoped_refptr<Thumbnail> task_thumbnail = |
| 692 make_scoped_refptr(new Thumbnail(*thumbnail)); |
| 693 |
| 694 base::Closure compression_task = |
| 695 base::Bind(&ThumbnailCache::CompressionTask, task_thumbnail); |
| 696 base::Closure post_compression_task = |
| 697 base::Bind(&ThumbnailCache::PostCompressionTask, |
| 698 weak_factory_.GetWeakPtr(), |
| 699 task_thumbnail); |
| 700 |
| 701 DCHECK(compression_thread_.message_loop_proxy()); |
| 702 compression_thread_.message_loop_proxy()->PostTaskAndReply( |
| 703 FROM_HERE, compression_task, post_compression_task); |
| 704 } |
| 705 } |
| 706 |
| 707 void ThumbnailCache::CompressionTask(scoped_refptr<Thumbnail> thumbnail) { |
| 708 thumbnail->Compress(); |
| 709 } |
| 710 |
| 711 void ThumbnailCache::PostCompressionTask( |
| 712 scoped_refptr<Thumbnail> compressed_thumbnail) { |
| 713 DCHECK(compressed_thumbnail); |
| 714 TabId tab_id = compressed_thumbnail->tab_id(); |
| 715 if (!compressed_thumbnail->HasCompressedRawData()) { |
| 716 // Compression has failed and we clean up. |
| 717 CleanupThumbnail(cache_.Remove(tab_id)); |
| 718 CleanupThumbnail(compressed_thumbnail); |
| 719 NotifyListenersOfThumbnailChange(tab_id, false); |
| 720 } else { |
| 721 // The thumbnail in |cache_| is not the same reference as |
| 722 // |compressed_thumbnail|. Replace the one in |cache_|. |
| 723 bool rebuild_thumbnail = false; |
| 724 if (cache_.Get(tab_id)) { |
| 725 cache_.Put(tab_id, compressed_thumbnail); |
| 726 rebuild_thumbnail = true; |
| 727 } |
| 728 |
| 729 if (rebuild_thumbnail) |
| 730 RebuildThumbnail(compressed_thumbnail, false); |
| 731 |
| 732 WriteThumbnailIfNecessary(compressed_thumbnail); |
| 733 CleanupThumbnail(compressed_thumbnail); |
| 734 NotifyListenersOfThumbnailChange(tab_id, true); |
| 735 } |
| 736 CompressNextThumbnail(); |
| 737 } |
| 738 |
| 739 void ThumbnailCache::RebuildThumbnail(scoped_refptr<Thumbnail> thumbnail, |
| 740 bool force_cleanup_cpu_data) { |
| 741 if (!thumbnail) |
| 742 return; |
| 743 |
| 744 thumbnail->RebuildThumbnail(); |
| 745 thumbnail->CleanupCPUData(force_cleanup_cpu_data); |
| 746 } |
| 747 |
| 748 bool ThumbnailCache::ShouldCleanupThumbnail( |
| 749 scoped_refptr<Thumbnail> thumbnail, |
| 750 const ExpiringThumbnailCache* forced_source) { |
| 751 scoped_refptr<Thumbnail> cache_entry = cache_.Get(thumbnail->tab_id()); |
| 752 scoped_refptr<Thumbnail> approx_entry = |
| 753 approximation_cache_.Get(thumbnail->tab_id()); |
| 754 return thumbnail->is_approximation() || |
| 755 ((cache_entry.get() != thumbnail.get() || forced_source == &cache_) && |
| 756 (approx_entry.get() != thumbnail.get() || |
| 757 forced_source == &approximation_cache_) && |
| 758 std::find(write_queue_.begin(), write_queue_.end(), thumbnail) == |
| 759 write_queue_.end() && |
| 760 std::find(compression_queue_.begin(), |
| 761 compression_queue_.end(), |
| 762 thumbnail) == compression_queue_.end()); |
| 763 } |
| 764 |
| 765 void ThumbnailCache::CleanupThumbnail(scoped_refptr<Thumbnail> thumbnail) { |
| 766 CleanupThumbnail(thumbnail, NULL); |
| 767 } |
| 768 |
| 769 void ThumbnailCache::CleanupThumbnail( |
| 770 scoped_refptr<Thumbnail> thumbnail, |
| 771 const ExpiringThumbnailCache* forced_source) { |
| 772 if (!thumbnail) |
| 773 return; |
| 774 |
| 775 if (ShouldCleanupThumbnail(thumbnail, forced_source)) { |
| 776 thumbnail->CleanupThumbnail(); |
| 777 thumbnail->CleanupCPUData(false); |
| 778 } |
| 779 } |
| 780 |
| 781 void ThumbnailCache::NotifyListenersOfThumbnailChange(TabId tab_id, |
| 782 bool upgrade) { |
| 783 NotifyListenersOfUIResourceUpdate(tab_id); |
| 784 |
| 785 for (ThumbnailChangeListenerSet::iterator iter = |
| 786 thumbnail_change_listeners_.begin(); |
| 787 iter != thumbnail_change_listeners_.end(); |
| 788 iter++) { |
| 789 (*iter)->OnThumbnailChanged(tab_id, upgrade); |
| 790 } |
| 791 } |
| 792 |
| 793 void ThumbnailCache::NotifyListenersOfUIResourceUpdate(TabId tab_id) { |
| 794 scoped_refptr<Thumbnail> thumbnail = GetThumbnail(tab_id); |
| 795 for (ThumbnailChangeListenerSet::iterator iter = |
| 796 thumbnail_change_listeners_.begin(); |
| 797 iter != thumbnail_change_listeners_.end(); |
| 798 iter++) { |
| 799 if (thumbnail) { |
| 800 (*iter)->OnUIResourceUpdated( |
| 801 tab_id, |
| 802 thumbnail->ui_resource_id(), |
| 803 thumbnail->GetContentSize(), |
| 804 gfx::ScaleSize(thumbnail->data_size(), 1.f / thumbnail->scale())); |
| 805 } else { |
| 806 (*iter)->OnUIResourceUpdated(tab_id, 0, gfx::Size(), gfx::SizeF()); |
| 807 } |
| 808 } |
| 809 } |
| 810 |
| 811 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const { |
| 812 return disk_cache_path_.Append(base::IntToString(tab_id)); |
| 813 } |
| 814 |
| 815 bool ThumbnailCache::ThumbnailInputFileExists(TabId tab_id) const { |
| 816 return base::PathExists(GetFilePath(tab_id)); |
| 817 } |
| 818 |
| 819 void ThumbnailCache::RemoveCorruptThumbnailSource(TabId tab_id) { |
| 820 RemoveFromDisk(tab_id); |
| 821 } |
| 822 |
| 823 void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) { |
| 824 if (cache_.Get(tab_id) || |
| 825 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == |
| 826 visible_ids_.end() || |
| 827 cache_.size() < cache_.MaximumCacheSize()) { |
| 828 return; |
| 829 } |
| 830 |
| 831 RemoveThumbnailFromCache(); |
| 832 } |
| 833 |
| 834 void ThumbnailCache::RemoveThumbnailFromCache() { |
| 835 TabId key_to_remove; |
| 836 bool found_key_to_remove = false; |
| 837 |
| 838 // 1. Find a cached item not in this list |
| 839 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); |
| 840 iter != cache_.end(); |
| 841 iter++) { |
| 842 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) == |
| 843 visible_ids_.end()) { |
| 844 key_to_remove = iter->first; |
| 845 found_key_to_remove = true; |
| 846 break; |
| 847 } |
| 848 } |
| 849 |
| 850 if (!found_key_to_remove) { |
| 851 // 2. Find the least important id we can remove. |
| 852 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin(); |
| 853 riter != visible_ids_.rend(); |
| 854 riter++) { |
| 855 if (cache_.Get(*riter)) { |
| 856 key_to_remove = *riter; |
| 857 break; |
| 858 found_key_to_remove = true; |
| 859 } |
| 860 } |
| 861 } |
| 862 |
| 863 if (found_key_to_remove) { |
| 864 CleanupThumbnail(cache_.Remove(key_to_remove)); |
| 865 NotifyListenersOfThumbnailChange(key_to_remove, false); |
| 866 } |
| 867 } |
| 868 |
| 869 void ThumbnailCache::AddIdToVisibleIds(TabId tab_id) { |
| 870 if (std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) != |
| 871 visible_ids_.end()) |
| 872 return; |
| 873 |
| 874 if (visible_ids_.size() > cache_.MaximumCacheSize() - 1) |
| 875 visible_ids_.pop_back(); |
| 876 |
| 877 visible_ids_.push_front(tab_id); |
| 878 } |
| 879 |
| 880 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() { |
| 881 } |
| 882 |
| 883 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData( |
| 884 const base::Time& current_time, |
| 885 const GURL& url) |
| 886 : capture_time_(current_time), url_(url) { |
| 887 } |
| 888 |
| 889 void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) { |
| 890 base::Closure remove_task = |
| 891 base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask, |
| 892 disk_cache_path_, |
| 893 min_id); |
| 894 content::BrowserThread::PostTask( |
| 895 content::BrowserThread::FILE, FROM_HERE, remove_task); |
| 896 } |
| 897 |
| 898 void ThumbnailCache::RemoveFromDisk(TabId tab_id) { |
| 899 base::Closure remove_task = |
| 900 base::Bind(&ThumbnailCache::RemoveFromDiskTask, GetFilePath(tab_id)); |
| 901 content::BrowserThread::PostTask( |
| 902 content::BrowserThread::FILE, FROM_HERE, remove_task); |
| 903 } |
| 904 |
| 905 void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) { |
| 906 base::DeleteFile(file_path, false); |
| 907 } |
| 908 |
| 909 void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask( |
| 910 const base::FilePath& dir_path, |
| 911 TabId min_id) { |
| 912 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); |
| 913 while (true) { |
| 914 base::FilePath path = enumerator.Next(); |
| 915 if (path.empty()) |
| 916 break; |
| 917 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); |
| 918 TabId tab_id; |
| 919 bool success = base::StringToInt(info.GetName().value(), &tab_id); |
| 920 if (success && tab_id >= min_id) |
| 921 base::DeleteFile(path, false); |
| 922 } |
| 923 } |
| 924 |
| 925 void ThumbnailCache::ThumbnailRequested(TabId tab_id) { |
| 926 scoped_refptr<Thumbnail> thumbnail = GetThumbnail(tab_id); |
| 927 if (thumbnail) { |
| 928 if (!thumbnail->RebuildThumbnail()) |
| 929 return; |
| 930 cc::UIResourceId ui_resource_id = thumbnail->ui_resource_id(); |
| 931 if (!ui_resource_id) |
| 932 return; |
| 933 NotifyListenersOfUIResourceUpdate(tab_id); |
| 934 } |
| 935 } |
| 936 |
| 937 void ThumbnailCache::OnUIResourcesAreInvalid() { |
| 938 RemoveThumbnailsAndScheduleReload(); |
| 939 } |
| 940 |
| 941 void ThumbnailCache::OnRecreateUIResources() { |
| 942 RemoveThumbnailsAndScheduleReload(); |
| 943 } |
| 944 |
| 945 base::Time ThumbnailCache::CurrentTime() { |
| 946 return base::Time::Now(); |
| 947 } |
| 948 |
| 949 ThumbnailCache::Thumbnail::Thumbnail( |
| 950 TabId tab_id, |
| 951 content::UIResourceProvider* ui_resource_provider) |
| 952 : tab_id_(tab_id), |
| 953 ui_resource_provider_(ui_resource_provider), |
| 954 ui_resource_id_(0), |
| 955 format_(DECOMPRESSED), |
| 956 rebuild_pending_(false), |
| 957 data_write_pending_(false), |
| 958 from_stream_(false) { |
| 959 DCHECK(ui_resource_provider); |
| 960 } |
| 961 |
| 962 ThumbnailCache::Thumbnail::Thumbnail( |
| 963 TabId tab_id, |
| 964 SkBitmap bitmap, |
| 965 float scale, |
| 966 content::UIResourceProvider* ui_resource_provider) |
| 967 : tab_id_(tab_id), |
| 968 raw_data_(bitmap), |
| 969 scale_(scale), |
| 970 ui_resource_provider_(ui_resource_provider), |
| 971 ui_resource_id_(0), |
| 972 format_(DECOMPRESSED), |
| 973 rebuild_pending_(false), |
| 974 data_write_pending_(false), |
| 975 from_stream_(false) { |
| 976 DCHECK(ui_resource_provider); |
| 977 SetRawThumbnailData(bitmap); |
| 978 } |
| 979 |
| 980 ThumbnailCache::Thumbnail::Thumbnail(const Thumbnail& thumbnail) |
| 981 : tab_id_(thumbnail.tab_id_), |
| 982 raw_data_(thumbnail.raw_data_), |
| 983 scale_(thumbnail.scale_), |
| 984 ui_resource_provider_(thumbnail.ui_resource_provider_), |
| 985 ui_resource_id_(thumbnail.ui_resource_id_), |
| 986 compressed_data_(thumbnail.compressed_data_), |
| 987 data_size_(thumbnail.data_size_), |
| 988 scaled_content_size_(thumbnail.scaled_content_size_), |
| 989 format_(thumbnail.format_), |
| 990 rebuild_pending_(thumbnail.rebuild_pending_), |
| 991 data_write_pending_(thumbnail.data_write_pending_), |
| 992 from_stream_(thumbnail.from_stream_) { |
| 993 } |
| 994 |
| 995 ThumbnailCache::Thumbnail::~Thumbnail() { |
| 996 } |
| 997 |
| 998 ThumbnailCache::ApproxThumbnail::ApproxThumbnail( |
| 999 TabId tab_id, |
| 1000 SkBitmap bitmap, |
| 1001 float scale, |
| 1002 content::UIResourceProvider* ui_resource_provider) |
| 1003 : Thumbnail(tab_id, bitmap, scale, ui_resource_provider) { |
| 1004 } |
| 1005 |
| 1006 void ThumbnailCache::Thumbnail::CleanupThumbnail() { |
| 1007 if (ui_resource_id_) { |
| 1008 ui_resource_provider_->DeleteUIResource(ui_resource_id_); |
| 1009 ui_resource_id_ = 0; |
| 1010 } |
| 1011 } |
| 1012 |
| 1013 bool ThumbnailCache::Thumbnail::AttemptToScheduleRebuildFromData() { |
| 1014 if (!raw_data_.empty() || |
| 1015 (compressed_data_ && !compressed_data_->info().isEmpty())) { |
| 1016 rebuild_pending_ = true; |
| 1017 return true; |
| 1018 } |
| 1019 return false; |
| 1020 } |
| 1021 |
| 1022 void ThumbnailCache::Thumbnail::CleanupCPUData(bool force) { |
| 1023 if (!CleanupRequired() && !force) |
| 1024 return; |
| 1025 raw_data_ = SkBitmap(); |
| 1026 compressed_data_.clear(); |
| 1027 } |
| 1028 |
| 1029 bool ThumbnailCache::Thumbnail::RebuildThumbnail() { |
| 1030 if (!rebuild_pending_) |
| 1031 return false; |
| 1032 CleanupThumbnail(); |
| 1033 if ((raw_data_.empty() && format_ == DECOMPRESSED) || |
| 1034 (!compressed_data_ && format_ == COMPRESSED)) |
| 1035 return false; |
| 1036 bool is_bitmap_transient = true; |
| 1037 if (format_ == COMPRESSED) { |
| 1038 ui_resource_id_ = ui_resource_provider_->GenerateCompressedUIResource( |
| 1039 compressed_data_, is_bitmap_transient); |
| 1040 } else { |
| 1041 ui_resource_id_ = ui_resource_provider_->GenerateUIResource( |
| 1042 raw_data_, is_bitmap_transient); |
| 1043 } |
| 1044 |
| 1045 rebuild_pending_ = false; |
| 1046 CleanupCPUData(false); |
| 1047 return true; |
| 1048 } |
| 1049 |
| 1050 bool ThumbnailCache::Thumbnail::CleanupRequired() { |
| 1051 return !(CompressionRequired() || DataWriteRequired() || rebuild_pending_ || |
| 1052 (raw_data_.empty() && !compressed_data_)); |
| 1053 } |
| 1054 |
| 1055 bool ThumbnailCache::Thumbnail::CompressionRequired() const { |
| 1056 return kEnableCompression && !compressed_data_ && !raw_data_.empty(); |
| 1057 } |
| 1058 |
| 1059 bool ThumbnailCache::Thumbnail::DataWriteRequired() const { |
| 1060 return data_write_pending_ && !from_stream_; |
| 1061 } |
| 1062 |
| 1063 bool ThumbnailCache::Thumbnail::HasCompressedRawData() const { |
| 1064 return compressed_data_; |
| 1065 } |
| 1066 |
| 1067 bool ThumbnailCache::Thumbnail::IsValid() const { |
| 1068 return ui_resource_provider_ && scale_ > 0 && !data_size_.IsEmpty() && |
| 1069 !scaled_content_size_.IsEmpty() && |
| 1070 (ui_resource_id_ || !raw_data_.empty() || compressed_data_); |
| 1071 } |
| 1072 |
| 1073 gfx::Size ThumbnailCache::Thumbnail::GetContentSize() const { |
| 1074 return gfx::ToRoundedSize(gfx::ScaleSize(scaled_content_size_, 1.f / scale_)); |
| 1075 } |
| 1076 |
| 1077 void ThumbnailCache::Thumbnail::Compress() { |
| 1078 SkBitmap raw_data = raw_data_; |
| 1079 if (CompressionRequired() && format_ == DECOMPRESSED && !raw_data.empty()) { |
| 1080 SkAutoLockPixels raw_data_lock(raw_data); |
| 1081 gfx::Size raw_data_size(raw_data.width(), raw_data.height()); |
| 1082 DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config); |
| 1083 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. |
| 1084 size_t stride = pixel_size * raw_data_size.width(); |
| 1085 |
| 1086 gfx::Size encoded_size = GetEncodedSize(raw_data_size); |
| 1087 size_t encoded_bytes = |
| 1088 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); |
| 1089 SkImageInfo info = {encoded_size.width(), |
| 1090 encoded_size.height(), |
| 1091 kUnknown_SkColorType, |
| 1092 kUnpremul_SkAlphaType}; |
| 1093 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( |
| 1094 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); |
| 1095 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( |
| 1096 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| 1097 |
| 1098 etc1_pixel_ref->lockPixels(); |
| 1099 bool success = etc1_encode_image( |
| 1100 reinterpret_cast<unsigned char*>(raw_data.getPixels()), |
| 1101 raw_data_size.width(), |
| 1102 raw_data_size.height(), |
| 1103 pixel_size, |
| 1104 stride, |
| 1105 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), |
| 1106 encoded_size.width(), |
| 1107 encoded_size.height()); |
| 1108 etc1_pixel_ref->unlockPixels(); |
| 1109 if (success) { |
| 1110 etc1_pixel_ref->setImmutable(); |
| 1111 SetCompressedThumbnailData(etc1_pixel_ref, raw_data_size); |
| 1112 } else { |
| 1113 Invalidate(); |
| 1114 } |
| 1115 } |
| 1116 } |
| 1117 |
| 1118 bool ThumbnailCache::Thumbnail::WriteThumbnailToFile(base::File& file) { |
| 1119 // We assume caller has already initialized the file and will also close it. |
| 1120 DCHECK(file.IsValid()); |
| 1121 |
| 1122 if (DataWriteRequired() && format_ == COMPRESSED && compressed_data_) { |
| 1123 if (kEnableCompression) { |
| 1124 compressed_data_->lockPixels(); |
| 1125 bool success = true; |
| 1126 int scaled_content_width = scaled_content_size_.width(); |
| 1127 int scaled_content_height = scaled_content_size_.height(); |
| 1128 int data_width = data_size_.width(); |
| 1129 int data_height = data_size_.height(); |
| 1130 |
| 1131 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), |
| 1132 sizeof(int)) < 0 || |
| 1133 file.WriteAtCurrentPos( |
| 1134 reinterpret_cast<const char*>(&scaled_content_width), |
| 1135 sizeof(int)) < 0 || |
| 1136 file.WriteAtCurrentPos( |
| 1137 reinterpret_cast<const char*>(&scaled_content_height), |
| 1138 sizeof(int)) < 0 || |
| 1139 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), |
| 1140 sizeof(int)) < 0 || |
| 1141 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), |
| 1142 sizeof(int)) < 0 || |
| 1143 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale_), |
| 1144 sizeof(float)) < 0) { |
| 1145 success = false; |
| 1146 } |
| 1147 |
| 1148 size_t compressed_bytes = |
| 1149 etc1_get_encoded_data_size(data_size_.width(), data_size_.height()); |
| 1150 |
| 1151 if (file.WriteAtCurrentPos( |
| 1152 reinterpret_cast<char*>(compressed_data_->pixels()), |
| 1153 compressed_bytes) < 0) |
| 1154 success = false; |
| 1155 |
| 1156 compressed_data_->unlockPixels(); |
| 1157 |
| 1158 if (!success) |
| 1159 Invalidate(); |
| 1160 |
| 1161 return success; |
| 1162 } else { |
| 1163 // In the original Java implementation, we store a jpg version when the |
| 1164 // |kEnableCompression| flag is off. Keep as NOTIMPLEMENTED for now. |
| 1165 NOTIMPLEMENTED(); |
| 1166 } |
| 1167 } |
| 1168 return false; |
| 1169 } |
| 1170 |
| 1171 bool ThumbnailCache::Thumbnail::ReadThumbnailFromFile(base::File& file) { |
| 1172 // We assume caller has already initialized the file and will also close it. |
| 1173 DCHECK(file.IsValid()); |
| 1174 |
| 1175 if (kEnableCompression) { |
| 1176 int key; |
| 1177 bool success = true; |
| 1178 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || |
| 1179 key != kCompressedKey) |
| 1180 success = false; |
| 1181 |
| 1182 int width, height; |
| 1183 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < |
| 1184 0 || |
| 1185 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < |
| 1186 0) |
| 1187 success = false; |
| 1188 scaled_content_size_ = gfx::Size(width, height); |
| 1189 |
| 1190 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < |
| 1191 0 || |
| 1192 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < |
| 1193 0) |
| 1194 success = false; |
| 1195 data_size_ = gfx::Size(width, height); |
| 1196 |
| 1197 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale_), sizeof(float)) < |
| 1198 0) |
| 1199 success = false; |
| 1200 |
| 1201 size_t compressed_bytes = |
| 1202 etc1_get_encoded_data_size(data_size_.width(), data_size_.height()); |
| 1203 |
| 1204 SkImageInfo info = {data_size_.width(), |
| 1205 data_size_.height(), |
| 1206 kUnknown_SkColorType, |
| 1207 kUnpremul_SkAlphaType}; |
| 1208 |
| 1209 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); |
| 1210 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), |
| 1211 compressed_bytes) < 0) |
| 1212 success = false; |
| 1213 |
| 1214 skia::RefPtr<SkData> etc1_pixel_data = |
| 1215 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); |
| 1216 compressed_data_ = skia::AdoptRef( |
| 1217 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| 1218 |
| 1219 format_ = COMPRESSED; |
| 1220 from_stream_ = true; |
| 1221 rebuild_pending_ = true; |
| 1222 data_write_pending_ = false; |
| 1223 |
| 1224 if (!success) |
| 1225 Invalidate(); |
| 1226 |
| 1227 return success; |
| 1228 } else { |
| 1229 NOTIMPLEMENTED(); |
| 1230 } |
| 1231 |
| 1232 return false; |
| 1233 } |
| 1234 |
| 1235 void ThumbnailCache::Thumbnail::Invalidate() { |
| 1236 CleanupCPUData(true); |
| 1237 ui_resource_id_ = 0; |
| 1238 data_size_ = gfx::Size(); |
| 1239 scaled_content_size_ = gfx::Size(); |
| 1240 } |
| 1241 |
| 1242 void ThumbnailCache::Thumbnail::SetCompressedThumbnailData( |
| 1243 skia::RefPtr<SkPixelRef> compressed_data, |
| 1244 gfx::Size scaled_content_size) { |
| 1245 compressed_data_ = compressed_data; |
| 1246 raw_data_ = SkBitmap(); |
| 1247 if (compressed_data) { |
| 1248 data_size_ = gfx::Size(compressed_data_->info().width(), |
| 1249 compressed_data_->info().height()); |
| 1250 scaled_content_size_ = scaled_content_size; |
| 1251 format_ = COMPRESSED; |
| 1252 rebuild_pending_ = true; |
| 1253 data_write_pending_ = true; |
| 1254 } else { |
| 1255 data_size_ = gfx::Size(); |
| 1256 scaled_content_size_ = gfx::Size(); |
| 1257 rebuild_pending_ = false; |
| 1258 data_write_pending_ = false; |
| 1259 } |
| 1260 } |
| 1261 |
| 1262 void ThumbnailCache::Thumbnail::SetRawThumbnailData(SkBitmap bitmap) { |
| 1263 raw_data_ = bitmap; |
| 1264 compressed_data_.clear(); |
| 1265 data_size_ = gfx::Size(bitmap.width(), bitmap.height()); |
| 1266 scaled_content_size_ = data_size_; |
| 1267 format_ = DECOMPRESSED; |
| 1268 rebuild_pending_ = true; |
| 1269 data_write_pending_ = !kEnableCompression; |
| 1270 } |
| 1271 |
| 1272 ThumbnailCache::ThumbnailBitmap::ThumbnailBitmap() : scale_(0.f) { |
| 1273 } |
| 1274 |
| 1275 ThumbnailCache::ThumbnailBitmap::ThumbnailBitmap(SkBitmap bitmap, float scale) |
| 1276 : bitmap_(bitmap), scale_(scale) { |
| 1277 } |
| 1278 |
| 1279 bool ThumbnailCache::ThumbnailBitmap::IsValid() const { |
| 1280 return !bitmap_.empty() && scale_ > 0; |
| 1281 } |
| 1282 |
| 1283 ThumbnailCache::ThumbnailBitmap |
| 1284 ThumbnailCache::ThumbnailBitmap::CreateApproximation() const { |
| 1285 SkAutoLockPixels bitmap_lock(bitmap_); |
| 1286 float new_scale = 1.f / kApproximationScaleFactor; |
| 1287 |
| 1288 gfx::Size dst_size = gfx::ToFlooredSize( |
| 1289 gfx::ScaleSize(gfx::Size(bitmap_.width(), bitmap_.height()), new_scale)); |
| 1290 SkBitmap dst_bitmap; |
| 1291 dst_bitmap.setConfig( |
| 1292 bitmap_.getConfig(), dst_size.width(), dst_size.height()); |
| 1293 dst_bitmap.allocPixels(); |
| 1294 dst_bitmap.eraseColor(0); |
| 1295 SkAutoLockPixels dst_bitmap_lock(dst_bitmap); |
| 1296 |
| 1297 SkCanvas canvas(dst_bitmap); |
| 1298 canvas.scale(new_scale, new_scale); |
| 1299 canvas.drawBitmap(bitmap_, 0, 0, NULL); |
| 1300 dst_bitmap.setImmutable(); |
| 1301 |
| 1302 return ThumbnailBitmap(dst_bitmap, new_scale * scale_); |
| 1303 } |
| 1304 |
| 1305 ThumbnailCache::TabReadbackRequest::TabReadbackRequest( |
| 1306 TabId tab_id, |
| 1307 content::ContentViewCore* content_view_core, |
| 1308 float thumbnail_scale, |
| 1309 base::Callback<void(int, const ThumbnailBitmap&)> end_callback) |
| 1310 : tab_id_(tab_id), |
| 1311 content_view_core_(content_view_core), |
| 1312 thumbnail_scale_(thumbnail_scale), |
| 1313 end_callback_(end_callback), |
| 1314 drop_after_readback_(false), |
| 1315 weak_factory_(this) { |
| 1316 } |
| 1317 |
| 1318 void ThumbnailCache::TabReadbackRequest::Run() { |
| 1319 base::Callback<void(bool, const SkBitmap&)> result_callback = |
| 1320 base::Bind(&TabReadbackRequest::OnFinishGetTabThumbnailBitmap, |
| 1321 weak_factory_.GetWeakPtr()); |
| 1322 if (content_view_core_) { |
| 1323 DCHECK(content_view_core_->GetWebContents()); |
| 1324 content_view_core_->GetWebContents() |
| 1325 ->GetRenderViewHost() |
| 1326 ->LockBackingStore(); |
| 1327 |
| 1328 // Calling this method with an empty rect will return a bitmap of the size |
| 1329 // of the content. |
| 1330 content_view_core_->GetScaledContentBitmap(thumbnail_scale_, |
| 1331 SkBitmap::kARGB_8888_Config, |
| 1332 gfx::Rect(), |
| 1333 result_callback); |
| 1334 } else { |
| 1335 result_callback.Run(false, SkBitmap()); |
| 1336 } |
| 1337 } |
| 1338 |
| 1339 // This callback should be called on the UI thread. So it should be safe to |
| 1340 // call the thumbnail cache. |
| 1341 void ThumbnailCache::TabReadbackRequest::OnFinishGetTabThumbnailBitmap( |
| 1342 bool success, |
| 1343 const SkBitmap& bitmap) { |
| 1344 if (content_view_core_) { |
| 1345 DCHECK(content_view_core_->GetWebContents()); |
| 1346 content_view_core_->GetWebContents() |
| 1347 ->GetRenderViewHost() |
| 1348 ->UnlockBackingStore(); |
| 1349 } |
| 1350 if (success) { |
| 1351 SkBitmap local_bitmap = bitmap; |
| 1352 local_bitmap.setImmutable(); |
| 1353 end_callback_.Run(tab_id_, ThumbnailBitmap(local_bitmap, thumbnail_scale_)); |
| 1354 } else { |
| 1355 end_callback_.Run(tab_id_, ThumbnailBitmap()); |
| 1356 } |
| 1357 } |
OLD | NEW |