1 // Copyright (c) 2012 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/icon_loader.h"
6 
7 #include <stddef.h>
8 
9 #include <map>
10 #include <string>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "chrome/grit/theme_resources.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "media/media_buildflags.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "ui/base/layout.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/canvas.h"
27 #include "ui/gfx/codec/png_codec.h"
28 #include "ui/gfx/image/image.h"
29 #include "ui/gfx/image/image_skia.h"
30 #include "ui/gfx/image/image_skia_operations.h"
31 
32 namespace {
33 
34 // Used with GenerateImageWithSize() to indicate that the image shouldn't be
35 // resized.
36 const int kDoNotResize = -1;
37 
38 struct IdrBySize {
39   int idr_small;
40   int idr_normal;
41   int idr_large;
42 };
43 
44 // Performs mapping of <file extension, icon size> to icon resource IDs.
45 class IconMapper {
46  public:
47   IconMapper();
48 
49   // Lookup icon resource ID for a given filename |extension| and
50   // |icon_size|. Defaults to generic icons if there are no icons for the given
51   // extension.
52   int Lookup(const std::string& extension, IconLoader::IconSize icon_size);
53 
54  private:
55   typedef std::map<std::string, IdrBySize> ExtensionIconMap;
56 
57   ExtensionIconMap extension_icon_map_;
58 };
59 
60 const IdrBySize kAudioIdrs = {
61   IDR_FILETYPE_AUDIO,
62   IDR_FILETYPE_LARGE_AUDIO,
63   IDR_FILETYPE_LARGE_AUDIO
64 };
65 const IdrBySize kGenericIdrs = {
66   IDR_FILETYPE_GENERIC,
67   IDR_FILETYPE_LARGE_GENERIC,
68   IDR_FILETYPE_LARGE_GENERIC
69 };
70 const IdrBySize kImageIdrs = {
71   IDR_FILETYPE_IMAGE,
72   IDR_FILETYPE_IMAGE,
73   IDR_FILETYPE_IMAGE
74 };
75 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
76 const IdrBySize kPdfIdrs = {
77   IDR_FILETYPE_PDF,
78   IDR_FILETYPE_PDF,
79   IDR_FILETYPE_PDF
80 };
81 #endif
82 const IdrBySize kVideoIdrs = {
83   IDR_FILETYPE_VIDEO,
84   IDR_FILETYPE_LARGE_VIDEO,
85   IDR_FILETYPE_LARGE_VIDEO
86 };
87 
IconMapper()88 IconMapper::IconMapper() {
89   // The code below should match translation in
90   // ui/file_manager/file_manager/js/file_manager.js
91   // ui/file_manager/file_manager/css/file_manager.css
92   // 'audio': /\.(mp3|m4a|oga|ogg|wav)$/i,
93   // 'html': /\.(html?)$/i,
94   // 'image': /\.(bmp|gif|jpe?g|ico|png|webp)$/i,
95   // 'pdf' : /\.(pdf)$/i,
96   // 'text': /\.(pod|rst|txt|log)$/i,
97   // 'video': /\.(mov|mp4|m4v|mpe?g4?|ogm|ogv|ogx|webm)$/i
98 
99   const ExtensionIconMap::value_type kExtensionIdrBySizeData[] = {
100 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
101     std::make_pair(".m4a", kAudioIdrs),
102     std::make_pair(".mp3", kAudioIdrs),
103     std::make_pair(".pdf", kPdfIdrs),
104     std::make_pair(".3gp", kVideoIdrs),
105     std::make_pair(".avi", kVideoIdrs),
106     std::make_pair(".m4v", kVideoIdrs),
107     std::make_pair(".mov", kVideoIdrs),
108     std::make_pair(".mp4", kVideoIdrs),
109     std::make_pair(".mpeg", kVideoIdrs),
110     std::make_pair(".mpg", kVideoIdrs),
111     std::make_pair(".mpeg4", kVideoIdrs),
112     std::make_pair(".mpg4", kVideoIdrs),
113 #endif
114     std::make_pair(".flac", kAudioIdrs),
115     std::make_pair(".oga", kAudioIdrs),
116     std::make_pair(".ogg", kAudioIdrs),
117     std::make_pair(".wav", kAudioIdrs),
118     std::make_pair(".bmp", kImageIdrs),
119     std::make_pair(".gif", kImageIdrs),
120     std::make_pair(".ico", kImageIdrs),
121     std::make_pair(".jpeg", kImageIdrs),
122     std::make_pair(".jpg", kImageIdrs),
123     std::make_pair(".png", kImageIdrs),
124     std::make_pair(".webp", kImageIdrs),
125     std::make_pair(".ogm", kVideoIdrs),
126     std::make_pair(".ogv", kVideoIdrs),
127     std::make_pair(".ogx", kVideoIdrs),
128     std::make_pair(".webm", kVideoIdrs),
129   };
130 
131   const size_t kESize = base::size(kExtensionIdrBySizeData);
132   ExtensionIconMap source(&kExtensionIdrBySizeData[0],
133                           &kExtensionIdrBySizeData[kESize]);
134   extension_icon_map_.swap(source);
135 }
136 
Lookup(const std::string & extension,IconLoader::IconSize icon_size)137 int IconMapper::Lookup(const std::string& extension,
138                        IconLoader::IconSize icon_size) {
139   DCHECK(icon_size == IconLoader::SMALL ||
140          icon_size == IconLoader::NORMAL ||
141          icon_size == IconLoader::LARGE);
142   ExtensionIconMap::const_iterator it = extension_icon_map_.find(extension);
143   const IdrBySize& idrbysize =
144       ((it == extension_icon_map_.end()) ? kGenericIdrs : it->second);
145   int idr = -1;
146   switch (icon_size) {
147     case IconLoader::SMALL:  idr = idrbysize.idr_small;  break;
148     case IconLoader::NORMAL: idr = idrbysize.idr_normal; break;
149     case IconLoader::LARGE:  idr = idrbysize.idr_large;  break;
150     case IconLoader::ALL:
151     default:
152       NOTREACHED();
153   }
154   return idr;
155 }
156 
157 // Returns a copy of |source| that is |dip_size| in width and height. If
158 // |dip_size| is |kDoNotResize|, returns an unmodified copy of |source|.
159 // |source| must be a square image (width == height).
ResizeImage(const gfx::ImageSkia & source,int dip_size)160 gfx::ImageSkia ResizeImage(const gfx::ImageSkia& source, int dip_size) {
161   DCHECK(!source.isNull());
162   DCHECK(source.width() == source.height());
163 
164   if (dip_size == kDoNotResize || source.width() == dip_size)
165     return source;
166 
167   return gfx::ImageSkiaOperations::CreateResizedImage(source,
168       skia::ImageOperations::RESIZE_BEST, gfx::Size(dip_size, dip_size));
169 }
170 
IconSizeToDIPSize(IconLoader::IconSize size)171 int IconSizeToDIPSize(IconLoader::IconSize size) {
172   switch (size) {
173     case IconLoader::SMALL:  return 16;
174     case IconLoader::NORMAL: return 32;
175     case IconLoader::LARGE:  // fallthrough
176       // On ChromeOS, we consider LARGE to mean "the largest image we have."
177       // Since we have already chosen the largest applicable image resource, we
178       // return the image as-is.
179     case IconLoader::ALL:    // fallthrough
180     default:
181       return kDoNotResize;
182   }
183 }
184 
185 }  // namespace
186 
187 // static
GroupForFilepath(const base::FilePath & file_path)188 IconLoader::IconGroup IconLoader::GroupForFilepath(
189     const base::FilePath& file_path) {
190   return base::ToLowerASCII(file_path.Extension());
191 }
192 
193 // static
GetReadIconTaskRunner()194 scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
195   // ReadIcon touches non thread safe ResourceBundle images, so it must be on
196   // the UI thread.
197   return content::GetUIThreadTaskRunner({});
198 }
199 
ReadIcon()200 void IconLoader::ReadIcon() {
201   static base::LazyInstance<IconMapper>::Leaky icon_mapper =
202       LAZY_INSTANCE_INITIALIZER;
203   int idr = icon_mapper.Get().Lookup(group_, icon_size_);
204   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
205   gfx::ImageSkia image_skia(ResizeImage(*(rb.GetImageNamed(idr)).ToImageSkia(),
206                                         IconSizeToDIPSize(icon_size_)));
207   image_skia.MakeThreadSafe();
208   gfx::Image image(image_skia);
209   target_task_runner_->PostTask(
210       FROM_HERE,
211       base::BindOnce(std::move(callback_), std::move(image), group_));
212   delete this;
213 }
214