• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/ui/webui/extension_icon_source.h"
6 
7 #include "base/callback.h"
8 #include "base/memory/ref_counted_memory.h"
9 #include "base/stl_util-inl.h"
10 #include "base/string_number_conversions.h"
11 #include "base/string_split.h"
12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
14 #include "base/task.h"
15 #include "base/threading/thread.h"
16 #include "chrome/browser/extensions/extension_prefs.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/extensions/extension.h"
20 #include "chrome/common/extensions/extension_resource.h"
21 #include "chrome/common/url_constants.h"
22 #include "grit/theme_resources.h"
23 #include "googleurl/src/gurl.h"
24 #include "skia/ext/image_operations.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/codec/png_codec.h"
27 #include "ui/gfx/color_utils.h"
28 #include "ui/gfx/skbitmap_operations.h"
29 #include "webkit/glue/image_decoder.h"
30 
31 namespace {
32 
BitmapToMemory(SkBitmap * image)33 scoped_refptr<RefCountedMemory> BitmapToMemory(SkBitmap* image) {
34   std::vector<unsigned char> output;
35   gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &output);
36 
37   scoped_refptr<RefCountedBytes> image_bytes(new RefCountedBytes);
38   image_bytes->data.resize(output.size());
39   std::copy(output.begin(), output.end(), image_bytes->data.begin());
40   return image_bytes;
41 }
42 
DesaturateImage(SkBitmap * image)43 void DesaturateImage(SkBitmap* image) {
44   color_utils::HSL shift = {-1, 0, 0.6};
45   *image = SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
46 }
47 
ToBitmap(const unsigned char * data,size_t size)48 SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
49   webkit_glue::ImageDecoder decoder;
50   SkBitmap* decoded = new SkBitmap();
51   *decoded = decoder.Decode(data, size);
52   return decoded;
53 }
54 
LoadImageByResourceId(int resource_id)55 SkBitmap* LoadImageByResourceId(int resource_id) {
56   std::string contents = ResourceBundle::GetSharedInstance()
57       .GetRawDataResource(resource_id).as_string();
58 
59   // Convert and return it.
60   const unsigned char* data =
61       reinterpret_cast<const unsigned char*>(contents.data());
62   return ToBitmap(data, contents.length());
63 }
64 
65 }  // namespace
66 
67 
ExtensionIconSource(Profile * profile)68 ExtensionIconSource::ExtensionIconSource(Profile* profile)
69     : DataSource(chrome::kChromeUIExtensionIconHost, MessageLoop::current()),
70       profile_(profile),
71       next_tracker_id_(0) {
72   tracker_.reset(new ImageLoadingTracker(this));
73 }
74 
75 struct ExtensionIconSource::ExtensionIconRequest {
76   int request_id;
77   const Extension* extension;
78   bool grayscale;
79   Extension::Icons size;
80   ExtensionIconSet::MatchType match;
81 };
82 
~ExtensionIconSource()83 ExtensionIconSource::~ExtensionIconSource() {
84   // Clean up all the temporary data we're holding for requests.
85   STLDeleteValues(&request_map_);
86 }
87 
88 // static
GetIconURL(const Extension * extension,Extension::Icons icon_size,ExtensionIconSet::MatchType match,bool grayscale)89 GURL ExtensionIconSource::GetIconURL(const Extension* extension,
90                                             Extension::Icons icon_size,
91                                             ExtensionIconSet::MatchType match,
92                                             bool grayscale) {
93   GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
94                                    chrome::kChromeUIExtensionIconURL,
95                                    extension->id().c_str(),
96                                    icon_size,
97                                    match,
98                                    grayscale ? "?grayscale=true" : ""));
99   CHECK(icon_url.is_valid());
100   return icon_url;
101 }
102 
GetMimeType(const std::string &) const103 std::string ExtensionIconSource::GetMimeType(const std::string&) const {
104   // We need to explicitly return a mime type, otherwise if the user tries to
105   // drag the image they get no extension.
106   return "image/png";
107 }
108 
StartDataRequest(const std::string & path,bool is_incognito,int request_id)109 void ExtensionIconSource::StartDataRequest(const std::string& path,
110                                            bool is_incognito,
111                                            int request_id) {
112   // This is where everything gets started. First, parse the request and make
113   // the request data available for later.
114   if (!ParseData(path, request_id)) {
115     SendDefaultResponse(request_id);
116     return;
117   }
118 
119   ExtensionIconRequest* request = GetData(request_id);
120   ExtensionResource icon =
121       request->extension->GetIconResource(request->size, request->match);
122 
123   if (icon.relative_path().empty())
124     LoadIconFailed(request_id);
125   else
126     LoadExtensionImage(icon, request_id);
127 }
128 
LoadIconFailed(int request_id)129 void ExtensionIconSource::LoadIconFailed(int request_id) {
130   ExtensionIconRequest* request = GetData(request_id);
131   ExtensionResource icon =
132       request->extension->GetIconResource(request->size, request->match);
133 
134   if (request->size == Extension::EXTENSION_ICON_BITTY)
135     LoadFaviconImage(request_id);
136   else
137     LoadDefaultImage(request_id);
138 }
139 
GetDefaultAppImage()140 SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
141   if (!default_app_data_.get())
142     default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));
143 
144   return default_app_data_.get();
145 }
146 
GetDefaultExtensionImage()147 SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
148   if (!default_extension_data_.get())
149     default_extension_data_.reset(
150         LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
151 
152   return default_extension_data_.get();
153 }
154 
FinalizeImage(SkBitmap * image,int request_id)155 void ExtensionIconSource::FinalizeImage(SkBitmap* image,
156                                         int request_id) {
157   if (GetData(request_id)->grayscale)
158     DesaturateImage(image);
159 
160   ClearData(request_id);
161   SendResponse(request_id, BitmapToMemory(image));
162 }
163 
LoadDefaultImage(int request_id)164 void ExtensionIconSource::LoadDefaultImage(int request_id) {
165   ExtensionIconRequest* request = GetData(request_id);
166   SkBitmap* decoded = NULL;
167 
168   if (request->extension->is_app())
169     decoded = GetDefaultAppImage();
170   else
171     decoded = GetDefaultExtensionImage();
172 
173   *decoded = skia::ImageOperations::Resize(
174       *decoded, skia::ImageOperations::RESIZE_LANCZOS3,
175       request->size, request->size);
176 
177   FinalizeImage(decoded, request_id);
178 }
179 
LoadExtensionImage(const ExtensionResource & icon,int request_id)180 void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
181                                              int request_id) {
182   ExtensionIconRequest* request = GetData(request_id);
183   tracker_map_[next_tracker_id_++] = request_id;
184   tracker_->LoadImage(request->extension,
185                       icon,
186                       gfx::Size(request->size, request->size),
187                       ImageLoadingTracker::DONT_CACHE);
188 }
189 
LoadFaviconImage(int request_id)190 void ExtensionIconSource::LoadFaviconImage(int request_id) {
191   FaviconService* favicon_service =
192       profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
193   // Fall back to the default icons if the service isn't available.
194   if (favicon_service == NULL) {
195     LoadDefaultImage(request_id);
196     return;
197   }
198 
199   GURL favicon_url = GetData(request_id)->extension->GetFullLaunchURL();
200   FaviconService::Handle handle = favicon_service->GetFaviconForURL(
201       favicon_url,
202       history::FAVICON,
203       &cancelable_consumer_,
204       NewCallback(this, &ExtensionIconSource::OnFaviconDataAvailable));
205   cancelable_consumer_.SetClientData(favicon_service, handle, request_id);
206 }
207 
OnFaviconDataAvailable(FaviconService::Handle request_handle,history::FaviconData favicon)208 void ExtensionIconSource::OnFaviconDataAvailable(
209     FaviconService::Handle request_handle,
210     history::FaviconData favicon) {
211   int request_id = cancelable_consumer_.GetClientData(
212       profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), request_handle);
213   ExtensionIconRequest* request = GetData(request_id);
214 
215   // Fallback to the default icon if there wasn't a favicon.
216   if (!favicon.is_valid()) {
217     LoadDefaultImage(request_id);
218     return;
219   }
220 
221   if (!request->grayscale) {
222     // If we don't need a grayscale image, then we can bypass FinalizeImage
223     // to avoid unnecessary conversions.
224     ClearData(request_id);
225     SendResponse(request_id, favicon.image_data);
226   } else {
227     FinalizeImage(ToBitmap(favicon.image_data->front(),
228                            favicon.image_data->size()), request_id);
229   }
230 }
231 
OnImageLoaded(SkBitmap * image,const ExtensionResource & resource,int index)232 void ExtensionIconSource::OnImageLoaded(SkBitmap* image,
233                                         const ExtensionResource& resource,
234                                         int index) {
235   int request_id = tracker_map_[index];
236   tracker_map_.erase(tracker_map_.find(index));
237 
238   if (!image || image->empty())
239     LoadIconFailed(request_id);
240   else
241     FinalizeImage(image, request_id);
242 }
243 
ParseData(const std::string & path,int request_id)244 bool ExtensionIconSource::ParseData(const std::string& path,
245                                     int request_id) {
246   // Extract the parameters from the path by lower casing and splitting.
247   std::string path_lower = StringToLowerASCII(path);
248   std::vector<std::string> path_parts;
249 
250   base::SplitString(path_lower, '/', &path_parts);
251   if (path_lower.empty() || path_parts.size() < 3)
252     return false;
253 
254   std::string size_param = path_parts.at(1);
255   std::string match_param = path_parts.at(2);
256   match_param = match_param.substr(0, match_param.find('?'));
257 
258   // The icon size and match types are encoded as string representations of
259   // their enum values, so to get the enum back, we read the string as an int
260   // and then cast to the enum.
261   Extension::Icons size;
262   int size_num;
263   if (!base::StringToInt(size_param, &size_num))
264     return false;
265   size = static_cast<Extension::Icons>(size_num);
266 
267   ExtensionIconSet::MatchType match_type;
268   int match_num;
269   if (!base::StringToInt(match_param, &match_num))
270     return false;
271   match_type = static_cast<ExtensionIconSet::MatchType>(match_num);
272 
273   std::string extension_id = path_parts.at(0);
274   const Extension* extension =
275       profile_->GetExtensionService()->GetExtensionById(extension_id, true);
276   if (!extension)
277     return false;
278 
279   bool grayscale = path_lower.find("grayscale=true") != std::string::npos;
280 
281   SetData(request_id, extension, grayscale, size, match_type);
282 
283   return true;
284 }
285 
SendDefaultResponse(int request_id)286 void ExtensionIconSource::SendDefaultResponse(int request_id) {
287   // We send back the default application icon (not resized or desaturated)
288   // as the default response, like when there is no data.
289   ClearData(request_id);
290   SendResponse(request_id, BitmapToMemory(GetDefaultAppImage()));
291 }
292 
SetData(int request_id,const Extension * extension,bool grayscale,Extension::Icons size,ExtensionIconSet::MatchType match)293 void ExtensionIconSource::SetData(int request_id,
294                                   const Extension* extension,
295                                   bool grayscale,
296                                   Extension::Icons size,
297                                   ExtensionIconSet::MatchType match) {
298   ExtensionIconRequest* request = new ExtensionIconRequest();
299   request->request_id = request_id;
300   request->extension = extension;
301   request->grayscale = grayscale;
302   request->size = size;
303   request->match = match;
304   request_map_[request_id] = request;
305 }
306 
GetData(int request_id)307 ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
308     int request_id) {
309   return request_map_[request_id];
310 }
311 
ClearData(int request_id)312 void ExtensionIconSource::ClearData(int request_id) {
313   std::map<int, ExtensionIconRequest*>::iterator i =
314       request_map_.find(request_id);
315   if (i == request_map_.end())
316     return;
317 
318   delete i->second;
319   request_map_.erase(i);
320 }
321