Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / android / thumbnail / thumbnail_store.cc
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/thumbnail_store.h"
6
7 #include <algorithm>
8 #include <cmath>
9
10 #include "base/big_endian.h"
11 #include "base/files/file.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/worker_pool.h"
17 #include "base/time/time.h"
18 #include "content/public/browser/android/ui_resource_provider.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "third_party/android_opengl/etc1/etc1.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkData.h"
24 #include "third_party/skia/include/core/SkMallocPixelRef.h"
25 #include "third_party/skia/include/core/SkPixelRef.h"
26 #include "ui/gfx/android/device_display_info.h"
27 #include "ui/gfx/geometry/size_conversions.h"
28
29 namespace {
30
31 const float kApproximationScaleFactor = 4.f;
32 const base::TimeDelta kCaptureMinRequestTimeMs(
33     base::TimeDelta::FromMilliseconds(1000));
34
35 const int kCompressedKey = 0xABABABAB;
36 const int kCurrentExtraVersion = 1;
37
38 // Indicates whether we prefer to have more free CPU memory over GPU memory.
39 const bool kPreferCPUMemory = true;
40
41 size_t NextPowerOfTwo(size_t x) {
42   --x;
43   x |= x >> 1;
44   x |= x >> 2;
45   x |= x >> 4;
46   x |= x >> 8;
47   x |= x >> 16;
48   return x + 1;
49 }
50
51 size_t RoundUpMod4(size_t x) {
52   return (x + 3) & ~3;
53 }
54
55 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size, bool supports_npot) {
56   DCHECK(!bitmap_size.IsEmpty());
57   if (!supports_npot)
58     return gfx::Size(NextPowerOfTwo(bitmap_size.width()),
59                      NextPowerOfTwo(bitmap_size.height()));
60   else
61     return gfx::Size(RoundUpMod4(bitmap_size.width()),
62                      RoundUpMod4(bitmap_size.height()));
63 }
64
65 template<typename T>
66 bool ReadBigEndianFromFile(base::File& file, T* out) {
67   char buffer[sizeof(T)];
68   if (file.ReadAtCurrentPos(buffer, sizeof(T)) != sizeof(T))
69     return false;
70   base::ReadBigEndian(buffer, out);
71   return true;
72 }
73
74 template<typename T>
75 bool WriteBigEndianToFile(base::File& file, T val) {
76   char buffer[sizeof(T)];
77   base::WriteBigEndian(buffer, val);
78   return file.WriteAtCurrentPos(buffer, sizeof(T)) == sizeof(T);
79 }
80
81 bool ReadBigEndianFloatFromFile(base::File& file, float* out) {
82   char buffer[sizeof(float)];
83   if (file.ReadAtCurrentPos(buffer, sizeof(buffer)) != sizeof(buffer))
84     return false;
85
86 #if defined(ARCH_CPU_LITTLE_ENDIAN)
87   for (size_t i = 0; i < sizeof(float) / 2; i++) {
88     char tmp = buffer[i];
89     buffer[i] = buffer[sizeof(float) - 1 - i];
90     buffer[sizeof(float) - 1 - i] = tmp;
91   }
92 #endif
93   memcpy(out, buffer, sizeof(buffer));
94
95   return true;
96 }
97
98 bool WriteBigEndianFloatToFile(base::File& file, float val) {
99   char buffer[sizeof(float)];
100   memcpy(buffer, &val, sizeof(buffer));
101
102 #if defined(ARCH_CPU_LITTLE_ENDIAN)
103   for (size_t i = 0; i < sizeof(float) / 2; i++) {
104     char tmp = buffer[i];
105     buffer[i] = buffer[sizeof(float) - 1 - i];
106     buffer[sizeof(float) - 1 - i] = tmp;
107   }
108 #endif
109   return file.WriteAtCurrentPos(buffer, sizeof(buffer)) == sizeof(buffer);
110 }
111
112 }  // anonymous namespace
113
114 ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str,
115                                size_t default_cache_size,
116                                size_t approximation_cache_size,
117                                size_t compression_queue_max_size,
118                                size_t write_queue_max_size,
119                                bool use_approximation_thumbnail)
120     : disk_cache_path_(disk_cache_path_str),
121       compression_queue_max_size_(compression_queue_max_size),
122       write_queue_max_size_(write_queue_max_size),
123       use_approximation_thumbnail_(use_approximation_thumbnail),
124       compression_tasks_count_(0),
125       write_tasks_count_(0),
126       read_in_progress_(false),
127       cache_(default_cache_size),
128       approximation_cache_(approximation_cache_size),
129       ui_resource_provider_(NULL),
130       weak_factory_(this) {
131   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
132 }
133
134 ThumbnailStore::~ThumbnailStore() {
135   SetUIResourceProvider(NULL);
136 }
137
138 void ThumbnailStore::SetUIResourceProvider(
139     content::UIResourceProvider* ui_resource_provider) {
140   if (ui_resource_provider_ == ui_resource_provider)
141     return;
142
143   approximation_cache_.Clear();
144   cache_.Clear();
145
146   ui_resource_provider_ = ui_resource_provider;
147 }
148
149 void ThumbnailStore::AddThumbnailStoreObserver(
150     ThumbnailStoreObserver* observer) {
151   if (!observers_.HasObserver(observer))
152     observers_.AddObserver(observer);
153 }
154
155 void ThumbnailStore::RemoveThumbnailStoreObserver(
156     ThumbnailStoreObserver* observer) {
157   if (observers_.HasObserver(observer))
158     observers_.RemoveObserver(observer);
159 }
160
161 void ThumbnailStore::Put(TabId tab_id,
162                          const SkBitmap& bitmap,
163                          float thumbnail_scale) {
164   if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0)
165     return;
166
167   DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end());
168
169   base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time();
170   scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create(
171       tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this);
172   thumbnail->SetBitmap(bitmap);
173
174   RemoveFromReadQueue(tab_id);
175   MakeSpaceForNewItemIfNecessary(tab_id);
176   cache_.Put(tab_id, thumbnail.Pass());
177
178   if (use_approximation_thumbnail_) {
179     std::pair<SkBitmap, float> approximation =
180         CreateApproximation(bitmap, thumbnail_scale);
181     scoped_ptr<Thumbnail> approx_thumbnail = Thumbnail::Create(
182         tab_id, time_stamp, approximation.second, ui_resource_provider_, this);
183     approx_thumbnail->SetBitmap(approximation.first);
184     approximation_cache_.Put(tab_id, approx_thumbnail.Pass());
185   }
186   CompressThumbnailIfNecessary(tab_id, time_stamp, bitmap, thumbnail_scale);
187 }
188
189 void ThumbnailStore::Remove(TabId tab_id) {
190   cache_.Remove(tab_id);
191   approximation_cache_.Remove(tab_id);
192   thumbnail_meta_data_.erase(tab_id);
193   RemoveFromDisk(tab_id);
194   RemoveFromReadQueue(tab_id);
195 }
196
197 Thumbnail* ThumbnailStore::Get(TabId tab_id, bool force_disk_read) {
198   Thumbnail* thumbnail = cache_.Get(tab_id);
199   if (thumbnail) {
200     thumbnail->CreateUIResource();
201     return thumbnail;
202   }
203
204   if (force_disk_read &&
205       std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) !=
206           visible_ids_.end() &&
207       std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
208           read_queue_.end()) {
209     read_queue_.push_back(tab_id);
210     ReadNextThumbnail();
211   }
212
213   thumbnail = approximation_cache_.Get(tab_id);
214   if (thumbnail) {
215     thumbnail->CreateUIResource();
216     return thumbnail;
217   }
218
219   return NULL;
220 }
221
222 void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id) {
223   base::Closure remove_task =
224       base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask,
225                  disk_cache_path_,
226                  min_id);
227   content::BrowserThread::PostTask(
228       content::BrowserThread::FILE, FROM_HERE, remove_task);
229 }
230
231 void ThumbnailStore::InvalidateThumbnailIfChanged(TabId tab_id,
232                                                   const GURL& url) {
233   ThumbnailMetaDataMap::iterator meta_data_iter =
234       thumbnail_meta_data_.find(tab_id);
235   if (meta_data_iter == thumbnail_meta_data_.end()) {
236     thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url);
237   } else if (meta_data_iter->second.url() != url) {
238     Remove(tab_id);
239   }
240 }
241
242 bool ThumbnailStore::CheckAndUpdateThumbnailMetaData(TabId tab_id,
243                                                      const GURL& url) {
244   base::Time current_time = base::Time::Now();
245   ThumbnailMetaDataMap::iterator meta_data_iter =
246       thumbnail_meta_data_.find(tab_id);
247   if (meta_data_iter != thumbnail_meta_data_.end() &&
248       meta_data_iter->second.url() == url &&
249       (current_time - meta_data_iter->second.capture_time()) <
250           kCaptureMinRequestTimeMs) {
251     return false;
252   }
253
254   thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url);
255   return true;
256 }
257
258 void ThumbnailStore::UpdateVisibleIds(const TabIdList& priority) {
259   if (priority.empty()) {
260     visible_ids_.clear();
261     return;
262   }
263
264   size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize());
265   if (visible_ids_.size() == ids_size) {
266     // Early out if called with the same input as last time (We only care
267     // about the first mCache.MaximumCacheSize() entries).
268     bool lists_differ = false;
269     TabIdList::const_iterator visible_iter = visible_ids_.begin();
270     TabIdList::const_iterator priority_iter = priority.begin();
271     while (visible_iter != visible_ids_.end() &&
272            priority_iter != priority.end()) {
273       if (*priority_iter != *visible_iter) {
274         lists_differ = true;
275         break;
276       }
277       visible_iter++;
278       priority_iter++;
279     }
280
281     if (!lists_differ)
282       return;
283   }
284
285   read_queue_.clear();
286   visible_ids_.clear();
287   size_t count = 0;
288   TabIdList::const_iterator iter = priority.begin();
289   while (iter != priority.end() && count < ids_size) {
290     TabId tab_id = *iter;
291     visible_ids_.push_back(tab_id);
292     if (!cache_.Get(tab_id) &&
293         std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
294             read_queue_.end()) {
295       read_queue_.push_back(tab_id);
296     }
297     iter++;
298     count++;
299   }
300
301   ReadNextThumbnail();
302 }
303
304 void ThumbnailStore::DecompressThumbnailFromFile(
305     TabId tab_id,
306     const base::Callback<void(bool, SkBitmap)>&
307         post_decompress_callback) {
308   base::FilePath file_path = GetFilePath(tab_id);
309
310   base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>
311       decompress_task = base::Bind(
312           &ThumbnailStore::DecompressionTask, post_decompress_callback);
313
314   content::BrowserThread::PostTask(
315       content::BrowserThread::FILE,
316       FROM_HERE,
317       base::Bind(&ThumbnailStore::ReadTask, true, file_path, decompress_task));
318 }
319
320 void ThumbnailStore::RemoveFromDisk(TabId tab_id) {
321   base::FilePath file_path = GetFilePath(tab_id);
322   base::Closure task =
323       base::Bind(&ThumbnailStore::RemoveFromDiskTask, file_path);
324   content::BrowserThread::PostTask(
325       content::BrowserThread::FILE, FROM_HERE, task);
326 }
327
328 void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) {
329   if (base::PathExists(file_path))
330     base::DeleteFile(file_path, false);
331 }
332
333 void ThumbnailStore::RemoveFromDiskAtAndAboveIdTask(
334     const base::FilePath& dir_path,
335     TabId min_id) {
336   base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
337   while (true) {
338     base::FilePath path = enumerator.Next();
339     if (path.empty())
340       break;
341     base::FileEnumerator::FileInfo info = enumerator.GetInfo();
342     TabId tab_id;
343     bool success = base::StringToInt(info.GetName().value(), &tab_id);
344     if (success && tab_id >= min_id)
345       base::DeleteFile(path, false);
346   }
347 }
348
349 void ThumbnailStore::WriteThumbnailIfNecessary(
350     TabId tab_id,
351     skia::RefPtr<SkPixelRef> compressed_data,
352     float scale,
353     const gfx::Size& content_size) {
354   if (write_tasks_count_ >= write_queue_max_size_)
355     return;
356
357   write_tasks_count_++;
358
359   base::Callback<void()> post_write_task =
360       base::Bind(&ThumbnailStore::PostWriteTask, weak_factory_.GetWeakPtr());
361   content::BrowserThread::PostTask(content::BrowserThread::FILE,
362                                    FROM_HERE,
363                                    base::Bind(&ThumbnailStore::WriteTask,
364                                               GetFilePath(tab_id),
365                                               compressed_data,
366                                               scale,
367                                               content_size,
368                                               post_write_task));
369 }
370
371 void ThumbnailStore::CompressThumbnailIfNecessary(
372     TabId tab_id,
373     const base::Time& time_stamp,
374     const SkBitmap& bitmap,
375     float scale) {
376   if (compression_tasks_count_ >= compression_queue_max_size_) {
377     RemoveOnMatchedTimeStamp(tab_id, time_stamp);
378     return;
379   }
380
381   compression_tasks_count_++;
382
383   base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>
384       post_compression_task = base::Bind(&ThumbnailStore::PostCompressionTask,
385                                          weak_factory_.GetWeakPtr(),
386                                          tab_id,
387                                          time_stamp,
388                                          scale);
389
390   gfx::Size raw_data_size(bitmap.width(), bitmap.height());
391   gfx::Size encoded_size = GetEncodedSize(
392       raw_data_size, ui_resource_provider_->SupportsETC1NonPowerOfTwo());
393
394   base::WorkerPool::PostTask(FROM_HERE,
395                              base::Bind(&ThumbnailStore::CompressionTask,
396                                         bitmap,
397                                         encoded_size,
398                                         post_compression_task),
399                              true);
400 }
401
402 void ThumbnailStore::ReadNextThumbnail() {
403   if (read_queue_.empty() || read_in_progress_)
404     return;
405
406   TabId tab_id = read_queue_.front();
407   read_in_progress_ = true;
408
409   base::FilePath file_path = GetFilePath(tab_id);
410
411   base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>
412       post_read_task = base::Bind(
413           &ThumbnailStore::PostReadTask, weak_factory_.GetWeakPtr(), tab_id);
414
415   content::BrowserThread::PostTask(
416       content::BrowserThread::FILE,
417       FROM_HERE,
418       base::Bind(&ThumbnailStore::ReadTask, false, file_path, post_read_task));
419 }
420
421 void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id) {
422   if (cache_.Get(tab_id) ||
423       std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) ==
424           visible_ids_.end() ||
425       cache_.size() < cache_.MaximumCacheSize()) {
426     return;
427   }
428
429   TabId key_to_remove;
430   bool found_key_to_remove = false;
431
432   // 1. Find a cached item not in this list
433   for (ExpiringThumbnailCache::iterator iter = cache_.begin();
434        iter != cache_.end();
435        iter++) {
436     if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) ==
437         visible_ids_.end()) {
438       key_to_remove = iter->first;
439       found_key_to_remove = true;
440       break;
441     }
442   }
443
444   if (!found_key_to_remove) {
445     // 2. Find the least important id we can remove.
446     for (TabIdList::reverse_iterator riter = visible_ids_.rbegin();
447          riter != visible_ids_.rend();
448          riter++) {
449       if (cache_.Get(*riter)) {
450         key_to_remove = *riter;
451         break;
452         found_key_to_remove = true;
453       }
454     }
455   }
456
457   if (found_key_to_remove)
458     cache_.Remove(key_to_remove);
459 }
460
461 void ThumbnailStore::RemoveFromReadQueue(TabId tab_id) {
462   TabIdList::iterator read_iter =
463       std::find(read_queue_.begin(), read_queue_.end(), tab_id);
464   if (read_iter != read_queue_.end())
465     read_queue_.erase(read_iter);
466 }
467
468 void ThumbnailStore::InvalidateCachedThumbnail(Thumbnail* thumbnail) {
469   DCHECK(thumbnail);
470   TabId tab_id = thumbnail->tab_id();
471   cc::UIResourceId uid = thumbnail->ui_resource_id();
472
473   Thumbnail* cached_thumbnail = cache_.Get(tab_id);
474   if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid)
475     cache_.Remove(tab_id);
476
477   cached_thumbnail = approximation_cache_.Get(tab_id);
478   if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid)
479     approximation_cache_.Remove(tab_id);
480 }
481
482 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const {
483   return disk_cache_path_.Append(base::IntToString(tab_id));
484 }
485
486 namespace {
487
488 bool WriteToFile(base::File& file,
489                  const gfx::Size& content_size,
490                  const float scale,
491                  skia::RefPtr<SkPixelRef> compressed_data) {
492   if (!file.IsValid())
493     return false;
494
495   if (!WriteBigEndianToFile(file, kCompressedKey))
496     return false;
497
498   if (!WriteBigEndianToFile(file, content_size.width()))
499     return false;
500
501   if (!WriteBigEndianToFile(file, content_size.height()))
502     return false;
503
504   // Write ETC1 header.
505   compressed_data->lockPixels();
506
507   unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE];
508   etc1_pkm_format_header(etc1_buffer,
509                          compressed_data->info().width(),
510                          compressed_data->info().height());
511
512   int header_bytes_written = file.WriteAtCurrentPos(
513       reinterpret_cast<char*>(etc1_buffer), ETC_PKM_HEADER_SIZE);
514   if (header_bytes_written != ETC_PKM_HEADER_SIZE)
515     return false;
516
517   int data_size = etc1_get_encoded_data_size(
518       compressed_data->info().width(),
519       compressed_data->info().height());
520   int pixel_bytes_written = file.WriteAtCurrentPos(
521       reinterpret_cast<char*>(compressed_data->pixels()),
522       data_size);
523   if (pixel_bytes_written != data_size)
524     return false;
525
526   compressed_data->unlockPixels();
527
528   if (!WriteBigEndianToFile(file, kCurrentExtraVersion))
529     return false;
530
531   if (!WriteBigEndianFloatToFile(file, 1.f / scale))
532     return false;
533
534   return true;
535 }
536
537 }  // anonymous namespace
538
539 void ThumbnailStore::WriteTask(const base::FilePath& file_path,
540                                skia::RefPtr<SkPixelRef> compressed_data,
541                                float scale,
542                                const gfx::Size& content_size,
543                                const base::Callback<void()>& post_write_task) {
544   DCHECK(compressed_data);
545
546   base::File file(file_path,
547                   base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
548
549   bool success = WriteToFile(file,
550                              content_size,
551                              scale,
552                              compressed_data);
553
554   file.Close();
555
556   if (!success)
557     base::DeleteFile(file_path, false);
558
559   content::BrowserThread::PostTask(
560       content::BrowserThread::UI, FROM_HERE, post_write_task);
561 }
562
563 void ThumbnailStore::PostWriteTask() {
564   write_tasks_count_--;
565 }
566
567 void ThumbnailStore::CompressionTask(
568     SkBitmap raw_data,
569     gfx::Size encoded_size,
570     const base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>&
571         post_compression_task) {
572   skia::RefPtr<SkPixelRef> compressed_data;
573   gfx::Size content_size;
574
575   if (!raw_data.empty()) {
576     SkAutoLockPixels raw_data_lock(raw_data);
577     gfx::Size raw_data_size(raw_data.width(), raw_data.height());
578     size_t pixel_size = 4;  // Pixel size is 4 bytes for kARGB_8888_Config.
579     size_t stride = pixel_size * raw_data_size.width();
580
581     size_t encoded_bytes =
582         etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height());
583     SkImageInfo info = SkImageInfo::Make(encoded_size.width(),
584                                          encoded_size.height(),
585                                          kUnknown_SkColorType,
586                                          kUnpremul_SkAlphaType);
587     skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef(
588         SkData::NewUninitialized(encoded_bytes));
589     skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef(
590         SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
591
592     etc1_pixel_ref->lockPixels();
593     bool success = etc1_encode_image(
594         reinterpret_cast<unsigned char*>(raw_data.getPixels()),
595         raw_data_size.width(),
596         raw_data_size.height(),
597         pixel_size,
598         stride,
599         reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()),
600         encoded_size.width(),
601         encoded_size.height());
602     etc1_pixel_ref->setImmutable();
603     etc1_pixel_ref->unlockPixels();
604
605     if (success) {
606       compressed_data = etc1_pixel_ref;
607       content_size = raw_data_size;
608     }
609   }
610
611   content::BrowserThread::PostTask(
612       content::BrowserThread::UI,
613       FROM_HERE,
614       base::Bind(post_compression_task, compressed_data, content_size));
615 }
616
617 void ThumbnailStore::PostCompressionTask(
618     TabId tab_id,
619     const base::Time& time_stamp,
620     float scale,
621     skia::RefPtr<SkPixelRef> compressed_data,
622     const gfx::Size& content_size) {
623   compression_tasks_count_--;
624   if (!compressed_data) {
625     RemoveOnMatchedTimeStamp(tab_id, time_stamp);
626     return;
627   }
628
629   Thumbnail* thumbnail = cache_.Get(tab_id);
630   if (thumbnail) {
631     if (thumbnail->time_stamp() != time_stamp)
632       return;
633     thumbnail->SetCompressedBitmap(compressed_data, content_size);
634     thumbnail->CreateUIResource();
635   }
636   WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size);
637 }
638
639 namespace {
640
641 bool ReadFromFile(base::File& file,
642                   gfx::Size* out_content_size,
643                   float* out_scale,
644                   skia::RefPtr<SkPixelRef>* out_pixels) {
645   if (!file.IsValid())
646     return false;
647
648   int key = 0;
649   if (!ReadBigEndianFromFile(file, &key))
650     return false;
651
652   if (key != kCompressedKey)
653     return false;
654
655   int content_width = 0;
656   if (!ReadBigEndianFromFile(file, &content_width) || content_width <= 0)
657     return false;
658
659   int content_height = 0;
660   if (!ReadBigEndianFromFile(file, &content_height) || content_height <= 0)
661     return false;
662
663   out_content_size->SetSize(content_width, content_height);
664
665   // Read ETC1 header.
666   int header_bytes_read = 0;
667   unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE];
668   header_bytes_read = file.ReadAtCurrentPos(
669       reinterpret_cast<char*>(etc1_buffer),
670       ETC_PKM_HEADER_SIZE);
671   if (header_bytes_read != ETC_PKM_HEADER_SIZE)
672     return false;
673
674   if (!etc1_pkm_is_valid(etc1_buffer))
675     return false;
676
677   int raw_width = 0;
678   raw_width = etc1_pkm_get_width(etc1_buffer);
679   if (raw_width <= 0)
680     return false;
681
682   int raw_height = 0;
683   raw_height = etc1_pkm_get_height(etc1_buffer);
684   if (raw_height <= 0)
685     return false;
686
687   // Do some simple sanity check validation.  We can't have thumbnails larger
688   // than the max display size of the screen.  We also can't have etc1 texture
689   // data larger than the next power of 2 up from that.
690   gfx::DeviceDisplayInfo display_info;
691   int max_dimension = std::max(display_info.GetDisplayWidth(),
692                                display_info.GetDisplayHeight());
693
694   if (content_width > max_dimension
695       || content_height > max_dimension
696       || static_cast<size_t>(raw_width) > NextPowerOfTwo(max_dimension)
697       || static_cast<size_t>(raw_height) > NextPowerOfTwo(max_dimension)) {
698     return false;
699   }
700
701   int data_size = etc1_get_encoded_data_size(raw_width, raw_height);
702   skia::RefPtr<SkData> etc1_pixel_data =
703       skia::AdoptRef(SkData::NewUninitialized(data_size));
704
705   int pixel_bytes_read = file.ReadAtCurrentPos(
706       reinterpret_cast<char*>(etc1_pixel_data->writable_data()),
707       data_size);
708
709   if (pixel_bytes_read != data_size)
710     return false;
711
712   SkImageInfo info = SkImageInfo::Make(raw_width,
713                                        raw_height,
714                                        kUnknown_SkColorType,
715                                        kUnpremul_SkAlphaType);
716
717   *out_pixels = skia::AdoptRef(
718       SkMallocPixelRef::NewWithData(info,
719                                     0,
720                                     NULL,
721                                     etc1_pixel_data.get()));
722
723   int extra_data_version = 0;
724   if (!ReadBigEndianFromFile(file, &extra_data_version))
725     return false;
726
727   *out_scale = 1.f;
728   if (extra_data_version == 1) {
729     if (!ReadBigEndianFloatFromFile(file, out_scale))
730       return false;
731
732     if (*out_scale == 0.f)
733       return false;
734
735     *out_scale = 1.f / *out_scale;
736   }
737
738   return true;
739 }
740
741 }// anonymous namespace
742
743 void ThumbnailStore::ReadTask(
744     bool decompress,
745     const base::FilePath& file_path,
746     const base::Callback<
747         void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>&
748         post_read_task) {
749   gfx::Size content_size;
750   float scale = 0.f;
751   skia::RefPtr<SkPixelRef> compressed_data;
752
753   if (base::PathExists(file_path)) {
754     base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
755
756
757     bool valid_contents = ReadFromFile(file,
758                                        &content_size,
759                                        &scale,
760                                        &compressed_data);
761     file.Close();
762
763     if (!valid_contents) {
764       content_size.SetSize(0, 0);
765             scale = 0.f;
766             compressed_data.clear();
767             base::DeleteFile(file_path, false);
768     }
769   }
770
771   if (decompress) {
772     base::WorkerPool::PostTask(
773         FROM_HERE,
774         base::Bind(post_read_task, compressed_data, scale, content_size),
775         true);
776   } else {
777     content::BrowserThread::PostTask(
778         content::BrowserThread::UI,
779         FROM_HERE,
780         base::Bind(post_read_task, compressed_data, scale, content_size));
781   }
782 }
783
784 void ThumbnailStore::PostReadTask(TabId tab_id,
785                                   skia::RefPtr<SkPixelRef> compressed_data,
786                                   float scale,
787                                   const gfx::Size& content_size) {
788   read_in_progress_ = false;
789
790   TabIdList::iterator iter =
791       std::find(read_queue_.begin(), read_queue_.end(), tab_id);
792   if (iter == read_queue_.end()) {
793     ReadNextThumbnail();
794     return;
795   }
796
797   read_queue_.erase(iter);
798
799   if (!cache_.Get(tab_id) && compressed_data) {
800     ThumbnailMetaDataMap::iterator meta_iter =
801         thumbnail_meta_data_.find(tab_id);
802     base::Time time_stamp = base::Time::Now();
803     if (meta_iter != thumbnail_meta_data_.end())
804       time_stamp = meta_iter->second.capture_time();
805
806     MakeSpaceForNewItemIfNecessary(tab_id);
807     scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create(
808         tab_id, time_stamp, scale, ui_resource_provider_, this);
809     thumbnail->SetCompressedBitmap(compressed_data,
810                                    content_size);
811     if (kPreferCPUMemory)
812       thumbnail->CreateUIResource();
813
814     cache_.Put(tab_id, thumbnail.Pass());
815     NotifyObserversOfThumbnailRead(tab_id);
816   }
817
818   ReadNextThumbnail();
819 }
820
821 void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) {
822   FOR_EACH_OBSERVER(
823       ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id));
824 }
825
826 void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id,
827                                               const base::Time& time_stamp) {
828   // We remove the cached version if it matches the tab_id and the time_stamp.
829   Thumbnail* thumbnail = cache_.Get(tab_id);
830   Thumbnail* approx_thumbnail = approximation_cache_.Get(tab_id);
831   if ((thumbnail && thumbnail->time_stamp() == time_stamp) ||
832       (approx_thumbnail && approx_thumbnail->time_stamp() == time_stamp)) {
833     Remove(tab_id);
834   }
835   return;
836 }
837
838 void ThumbnailStore::DecompressionTask(
839     const base::Callback<void(bool, SkBitmap)>&
840         post_decompression_callback,
841     skia::RefPtr<SkPixelRef> compressed_data,
842     float scale,
843     const gfx::Size& content_size) {
844   SkBitmap raw_data_small;
845   bool success = false;
846
847   if (compressed_data.get()) {
848     gfx::Size buffer_size = gfx::Size(compressed_data->info().width(),
849                                       compressed_data->info().height());
850
851     SkBitmap raw_data;
852     raw_data.allocPixels(SkImageInfo::Make(buffer_size.width(),
853                                            buffer_size.height(),
854                                            kRGBA_8888_SkColorType,
855                                            kOpaque_SkAlphaType));
856     SkAutoLockPixels raw_data_lock(raw_data);
857     compressed_data->lockPixels();
858     success = etc1_decode_image(
859         reinterpret_cast<unsigned char*>(compressed_data->pixels()),
860         reinterpret_cast<unsigned char*>(raw_data.getPixels()),
861         buffer_size.width(),
862         buffer_size.height(),
863         raw_data.bytesPerPixel(),
864         raw_data.rowBytes());
865     compressed_data->unlockPixels();
866     raw_data.setImmutable();
867
868     if (!success) {
869       // Leave raw_data_small empty for consistency with other failure modes.
870     } else if (content_size == buffer_size) {
871       // Shallow copy the pixel reference.
872       raw_data_small = raw_data;
873     } else {
874       // The content size is smaller than the buffer size (likely because of
875       // a power-of-two rounding), so deep copy the bitmap.
876       raw_data_small.allocPixels(SkImageInfo::Make(content_size.width(),
877                                                    content_size.height(),
878                                                    kRGBA_8888_SkColorType,
879                                                    kOpaque_SkAlphaType));
880       SkAutoLockPixels raw_data_small_lock(raw_data_small);
881       SkCanvas small_canvas(raw_data_small);
882       small_canvas.drawBitmap(raw_data, 0, 0);
883       raw_data_small.setImmutable();
884     }
885   }
886
887   content::BrowserThread::PostTask(
888       content::BrowserThread::UI,
889       FROM_HERE,
890       base::Bind(post_decompression_callback, success, raw_data_small));
891 }
892
893 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() {
894 }
895
896 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData(
897     const base::Time& current_time,
898     const GURL& url)
899     : capture_time_(current_time), url_(url) {
900 }
901
902 std::pair<SkBitmap, float> ThumbnailStore::CreateApproximation(
903     const SkBitmap& bitmap,
904     float scale) {
905   DCHECK(!bitmap.empty());
906   DCHECK_GT(scale, 0);
907   SkAutoLockPixels bitmap_lock(bitmap);
908   float new_scale = 1.f / kApproximationScaleFactor;
909
910   gfx::Size dst_size = gfx::ToFlooredSize(
911       gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale));
912   SkBitmap dst_bitmap;
913   dst_bitmap.allocPixels(SkImageInfo::Make(dst_size.width(),
914                                            dst_size.height(),
915                                            bitmap.info().fColorType,
916                                            bitmap.info().fAlphaType));
917   dst_bitmap.eraseColor(0);
918   SkAutoLockPixels dst_bitmap_lock(dst_bitmap);
919
920   SkCanvas canvas(dst_bitmap);
921   canvas.scale(new_scale, new_scale);
922   canvas.drawBitmap(bitmap, 0, 0, NULL);
923   dst_bitmap.setImmutable();
924
925   return std::make_pair(dst_bitmap, new_scale * scale);
926 }