• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "tests/cefclient/browser/image_cache.h"
6 
7 #include "tests/shared/browser/file_util.h"
8 #include "tests/shared/browser/resource_util.h"
9 
10 namespace client {
11 
12 namespace {
13 
14 const char kEmptyId[] = "__empty";
15 
16 }  // namespace
17 
ImageCache()18 ImageCache::ImageCache() {}
19 
~ImageCache()20 ImageCache::~ImageCache() {
21   CEF_REQUIRE_UI_THREAD();
22 }
23 
ImageRep(const std::string & path,float scale_factor)24 ImageCache::ImageRep::ImageRep(const std::string& path, float scale_factor)
25     : path_(path), scale_factor_(scale_factor) {
26   DCHECK(!path_.empty());
27   DCHECK_GT(scale_factor_, 0.0f);
28 }
29 
ImageInfo(const std::string & id,const ImageRepSet & reps,bool internal,bool force_reload)30 ImageCache::ImageInfo::ImageInfo(const std::string& id,
31                                  const ImageRepSet& reps,
32                                  bool internal,
33                                  bool force_reload)
34     : id_(id), reps_(reps), internal_(internal), force_reload_(force_reload) {
35 #ifndef NDEBUG
36   DCHECK(!id_.empty());
37   if (id_ != kEmptyId)
38     DCHECK(!reps_.empty());
39 #endif
40 }
41 
42 // static
Empty()43 ImageCache::ImageInfo ImageCache::ImageInfo::Empty() {
44   return ImageInfo(kEmptyId, ImageRepSet(), true, false);
45 }
46 
47 // static
Create1x(const std::string & id,const std::string & path_1x,bool internal)48 ImageCache::ImageInfo ImageCache::ImageInfo::Create1x(
49     const std::string& id,
50     const std::string& path_1x,
51     bool internal) {
52   ImageRepSet reps;
53   reps.push_back(ImageRep(path_1x, 1.0f));
54   return ImageInfo(id, reps, internal, false);
55 }
56 
57 // static
Create2x(const std::string & id,const std::string & path_1x,const std::string & path_2x,bool internal)58 ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(
59     const std::string& id,
60     const std::string& path_1x,
61     const std::string& path_2x,
62     bool internal) {
63   ImageRepSet reps;
64   reps.push_back(ImageRep(path_1x, 1.0f));
65   reps.push_back(ImageRep(path_2x, 2.0f));
66   return ImageInfo(id, reps, internal, false);
67 }
68 
69 // static
Create2x(const std::string & id)70 ImageCache::ImageInfo ImageCache::ImageInfo::Create2x(const std::string& id) {
71   return Create2x(id, id + ".1x.png", id + ".2x.png", true);
72 }
73 
74 struct ImageCache::ImageContent {
ImageContentclient::ImageCache::ImageContent75   ImageContent() {}
76 
77   struct RepContent {
RepContentclient::ImageCache::ImageContent::RepContent78     RepContent(ImageType type, float scale_factor, const std::string& contents)
79         : type_(type), scale_factor_(scale_factor), contents_(contents) {}
80 
81     ImageType type_;
82     float scale_factor_;
83     std::string contents_;
84   };
85   typedef std::vector<RepContent> RepContentSet;
86   RepContentSet contents_;
87 
88   CefRefPtr<CefImage> image_;
89 };
90 
LoadImages(const ImageInfoSet & image_info,const LoadImagesCallback & callback)91 void ImageCache::LoadImages(const ImageInfoSet& image_info,
92                             const LoadImagesCallback& callback) {
93   DCHECK(!image_info.empty());
94   DCHECK(!callback.is_null());
95 
96   if (!CefCurrentlyOn(TID_UI)) {
97     CefPostTask(TID_UI, base::Bind(&ImageCache::LoadImages, this, image_info,
98                                    callback));
99     return;
100   }
101 
102   ImageSet images;
103   bool missing_images = false;
104 
105   ImageInfoSet::const_iterator it = image_info.begin();
106   for (; it != image_info.end(); ++it) {
107     const ImageInfo& info = *it;
108 
109     if (info.id_ == kEmptyId) {
110       // Image intentionally left empty.
111       images.push_back(nullptr);
112       continue;
113     }
114 
115     ImageMap::iterator it2 = image_map_.find(info.id_);
116     if (it2 != image_map_.end()) {
117       if (!info.force_reload_) {
118         // Image already exists.
119         images.push_back(it2->second);
120         continue;
121       }
122 
123       // Remove the existing image from the map.
124       image_map_.erase(it2);
125     }
126 
127     // Load the image.
128     images.push_back(nullptr);
129     if (!missing_images)
130       missing_images = true;
131   }
132 
133   if (missing_images) {
134     CefPostTask(TID_FILE_USER_BLOCKING,
135                 base::Bind(&ImageCache::LoadMissing, this, image_info, images,
136                            callback));
137   } else {
138     callback.Run(images);
139   }
140 }
141 
GetCachedImage(const std::string & image_id)142 CefRefPtr<CefImage> ImageCache::GetCachedImage(const std::string& image_id) {
143   CEF_REQUIRE_UI_THREAD();
144   DCHECK(!image_id.empty());
145 
146   ImageMap::const_iterator it = image_map_.find(image_id);
147   if (it != image_map_.end())
148     return it->second;
149 
150   return nullptr;
151 }
152 
153 // static
GetImageType(const std::string & path)154 ImageCache::ImageType ImageCache::GetImageType(const std::string& path) {
155   std::string ext = file_util::GetFileExtension(path);
156   if (ext.empty())
157     return TYPE_NONE;
158 
159   std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
160   if (ext == "png")
161     return TYPE_PNG;
162   if (ext == "jpg" || ext == "jpeg")
163     return TYPE_JPEG;
164 
165   return TYPE_NONE;
166 }
167 
LoadMissing(const ImageInfoSet & image_info,const ImageSet & images,const LoadImagesCallback & callback)168 void ImageCache::LoadMissing(const ImageInfoSet& image_info,
169                              const ImageSet& images,
170                              const LoadImagesCallback& callback) {
171   DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
172 
173   DCHECK_EQ(image_info.size(), images.size());
174 
175   ImageContentSet contents;
176 
177   ImageInfoSet::const_iterator it1 = image_info.begin();
178   ImageSet::const_iterator it2 = images.begin();
179   for (; it1 != image_info.end() && it2 != images.end(); ++it1, ++it2) {
180     const ImageInfo& info = *it1;
181     ImageContent content;
182     if (*it2 || info.id_ == kEmptyId) {
183       // Image already exists or is intentionally empty.
184       content.image_ = *it2;
185     } else {
186       LoadImageContents(info, &content);
187     }
188     contents.push_back(content);
189   }
190 
191   CefPostTask(TID_UI, base::Bind(&ImageCache::UpdateCache, this, image_info,
192                                  contents, callback));
193 }
194 
195 // static
LoadImageContents(const ImageInfo & info,ImageContent * content)196 bool ImageCache::LoadImageContents(const ImageInfo& info,
197                                    ImageContent* content) {
198   DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
199 
200   ImageRepSet::const_iterator it = info.reps_.begin();
201   for (; it != info.reps_.end(); ++it) {
202     const ImageRep& rep = *it;
203     ImageType rep_type;
204     std::string rep_contents;
205     if (!LoadImageContents(rep.path_, info.internal_, &rep_type,
206                            &rep_contents)) {
207       LOG(ERROR) << "Failed to load image " << info.id_ << " from path "
208                  << rep.path_;
209       return false;
210     }
211     content->contents_.push_back(
212         ImageContent::RepContent(rep_type, rep.scale_factor_, rep_contents));
213   }
214 
215   return true;
216 }
217 
218 // static
LoadImageContents(const std::string & path,bool internal,ImageType * type,std::string * contents)219 bool ImageCache::LoadImageContents(const std::string& path,
220                                    bool internal,
221                                    ImageType* type,
222                                    std::string* contents) {
223   DCHECK(!CefCurrentlyOn(TID_UI) && !CefCurrentlyOn(TID_IO));
224 
225   *type = GetImageType(path);
226   if (*type == TYPE_NONE)
227     return false;
228 
229   if (internal) {
230     if (!LoadBinaryResource(path.c_str(), *contents))
231       return false;
232   } else if (!file_util::ReadFileToString(path, contents)) {
233     return false;
234   }
235 
236   return !contents->empty();
237 }
238 
UpdateCache(const ImageInfoSet & image_info,const ImageContentSet & contents,const LoadImagesCallback & callback)239 void ImageCache::UpdateCache(const ImageInfoSet& image_info,
240                              const ImageContentSet& contents,
241                              const LoadImagesCallback& callback) {
242   CEF_REQUIRE_UI_THREAD();
243 
244   DCHECK_EQ(image_info.size(), contents.size());
245 
246   ImageSet images;
247 
248   ImageInfoSet::const_iterator it1 = image_info.begin();
249   ImageContentSet::const_iterator it2 = contents.begin();
250   for (; it1 != image_info.end() && it2 != contents.end(); ++it1, ++it2) {
251     const ImageInfo& info = *it1;
252     const ImageContent& content = *it2;
253     if (content.image_ || info.id_ == kEmptyId) {
254       // Image already exists or is intentionally empty.
255       images.push_back(content.image_);
256     } else {
257       CefRefPtr<CefImage> image = CreateImage(info.id_, content);
258       images.push_back(image);
259 
260       // Add the image to the map.
261       image_map_.insert(std::make_pair(info.id_, image));
262     }
263   }
264 
265   callback.Run(images);
266 }
267 
268 // static
CreateImage(const std::string & image_id,const ImageContent & content)269 CefRefPtr<CefImage> ImageCache::CreateImage(const std::string& image_id,
270                                             const ImageContent& content) {
271   CEF_REQUIRE_UI_THREAD();
272 
273   // Shouldn't be creating an image if one already exists.
274   DCHECK(!content.image_);
275 
276   if (content.contents_.empty())
277     return nullptr;
278 
279   CefRefPtr<CefImage> image = CefImage::CreateImage();
280 
281   ImageContent::RepContentSet::const_iterator it = content.contents_.begin();
282   for (; it != content.contents_.end(); ++it) {
283     const ImageContent::RepContent& rep = *it;
284     if (rep.type_ == TYPE_PNG) {
285       if (!image->AddPNG(rep.scale_factor_, rep.contents_.c_str(),
286                          rep.contents_.size())) {
287         LOG(ERROR) << "Failed to create image " << image_id << " for PNG@"
288                    << rep.scale_factor_;
289         return nullptr;
290       }
291     } else if (rep.type_ == TYPE_JPEG) {
292       if (!image->AddJPEG(rep.scale_factor_, rep.contents_.c_str(),
293                           rep.contents_.size())) {
294         LOG(ERROR) << "Failed to create image " << image_id << " for JPG@"
295                    << rep.scale_factor_;
296         return nullptr;
297       }
298     } else {
299       NOTREACHED();
300       return nullptr;
301     }
302   }
303 
304   return image;
305 }
306 
307 }  // namespace client
308