15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_icon_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/browser/image_loader.h"
11c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/constants.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
13c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/extension_icon_set.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/extension_resource.h"
15c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "extensions/common/manifest_handlers/icons_handler.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/image_operations.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/color_utils.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/favicon_size.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/size.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/skbitmap_operations.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper function to create a new bitmap with |padding| amount of empty space
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// around the original bitmap.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static SkBitmap ApplyPadding(const SkBitmap& source,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const gfx::Insets& padding) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<gfx::Canvas> result(
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new gfx::Canvas(gfx::Size(source.width() + padding.width(),
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                source.height() + padding.height()),
3568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                      1.0f,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      false));
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result->DrawImageInt(
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      gfx::ImageSkia::CreateFrom1xBitmap(source),
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0, 0, source.width(), source.height(),
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      padding.left(), padding.top(), source.width(), source.height(),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      false);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result->ExtractImageRep().sk_bitmap();
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionIconManager::ExtensionIconManager()
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : monochrome_(false),
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this)  {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionIconManager::~ExtensionIconManager() {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void ExtensionIconManager::LoadIcon(content::BrowserContext* context,
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    const extensions::Extension* extension) {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extensions::ExtensionResource icon_resource =
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::IconsInfo::GetIconResource(
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extension,
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extension_misc::EXTENSION_ICON_BITTY,
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          ExtensionIconSet::MATCH_BIGGER);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon_resource.extension_root().empty()) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Insert into pending_icons_ first because LoadImage can call us back
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // synchronously if the image is already cached.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pending_icons_.insert(extension->id());
661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    extensions::ImageLoader* loader = extensions::ImageLoader::Get(context);
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    loader->LoadImageAsync(extension, icon_resource,
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           gfx::Size(gfx::kFaviconSize, gfx::kFaviconSize),
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           base::Bind(
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               &ExtensionIconManager::OnImageLoaded,
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               weak_ptr_factory_.GetWeakPtr(),
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               extension->id()));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const SkBitmap& ExtensionIconManager::GetIcon(const std::string& extension_id) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkBitmap* result = NULL;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ContainsKey(icons_, extension_id)) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = &icons_[extension_id];
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnsureDefaultIcon();
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = &default_icon_;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(result);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(gfx::kFaviconSize + padding_.width(), result->width());
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(gfx::kFaviconSize + padding_.height(), result->height());
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return *result;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionIconManager::RemoveIcon(const std::string& extension_id) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icons_.erase(extension_id);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_icons_.erase(extension_id);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ExtensionIconManager::OnImageLoaded(const std::string& extension_id,
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const gfx::Image& image) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (image.IsEmpty())
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We may have removed the icon while waiting for it to load. In that case,
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // do nothing.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ContainsKey(pending_icons_, extension_id))
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_icons_.erase(extension_id);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icons_[extension_id] = ApplyTransforms(*image.ToSkBitmap());
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionIconManager::EnsureDefaultIcon() {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (default_icon_.empty()) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkBitmap src = rb.GetImageNamed(IDR_EXTENSIONS_SECTION).AsBitmap();
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default_icon_ = ApplyTransforms(src);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap ExtensionIconManager::ApplyTransforms(const SkBitmap& source) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap result = source;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result.width() != gfx::kFaviconSize ||
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result.height() != gfx::kFaviconSize) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = skia::ImageOperations::Resize(
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result, skia::ImageOperations::RESIZE_LANCZOS3,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gfx::kFaviconSize, gfx::kFaviconSize);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (monochrome_) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    color_utils::HSL shift = {-1, 0, 0.6};
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = SkBitmapOperations::CreateHSLShiftedBitmap(result, shift);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!padding_.empty())
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = ApplyPadding(result, padding_);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
137