Home | History | Annotate | Download | only in webui
      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 
     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 
     43 void DesaturateImage(SkBitmap* image) {
     44   color_utils::HSL shift = {-1, 0, 0.6};
     45   *image = SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
     46 }
     47 
     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 
     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 
     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 
     83 ExtensionIconSource::~ExtensionIconSource() {
     84   // Clean up all the temporary data we're holding for requests.
     85   STLDeleteValues(&request_map_);
     86 }
     87 
     88 // static
     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 
    103 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    307 ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
    308     int request_id) {
    309   return request_map_[request_id];
    310 }
    311 
    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