Home | History | Annotate | Download | only in base
      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 "base/mime_util.h"
      6 
      7 #include <gtk/gtk.h>
      8 #include <sys/time.h>
      9 #include <time.h>
     10 
     11 #include <cstdlib>
     12 #include <list>
     13 #include <map>
     14 #include <vector>
     15 
     16 #include "base/file_util.h"
     17 #include "base/logging.h"
     18 #include "base/memory/scoped_ptr.h"
     19 #include "base/memory/singleton.h"
     20 #include "base/message_loop.h"
     21 #include "base/string_split.h"
     22 #include "base/string_util.h"
     23 #include "base/synchronization/lock.h"
     24 #include "base/third_party/xdg_mime/xdgmime.h"
     25 #include "base/threading/thread_restrictions.h"
     26 
     27 namespace {
     28 
     29 // None of the XDG stuff is thread-safe, so serialize all accesss under
     30 // this lock.
     31 base::Lock g_mime_util_xdg_lock;
     32 
     33 class IconTheme;
     34 
     35 class MimeUtilConstants {
     36  public:
     37   static MimeUtilConstants* GetInstance() {
     38     return Singleton<MimeUtilConstants>::get();
     39   }
     40 
     41   // In seconds, specified by icon theme specs.
     42   const int kUpdateInterval;
     43 
     44   // Store icon directories and their mtimes.
     45   std::map<FilePath, int>* icon_dirs_;
     46 
     47   // Store icon formats.
     48   std::vector<std::string> icon_formats_;
     49 
     50   // Store loaded icon_theme.
     51   std::map<std::string, IconTheme*>* icon_themes_;
     52 
     53   static const size_t kDefaultThemeNum = 4;
     54 
     55   // The default theme.
     56   IconTheme* default_themes_[kDefaultThemeNum];
     57 
     58   time_t last_check_time_;
     59 
     60   // This is set by DetectGtkTheme(). We cache it so that we can access the
     61   // theme name from threads that aren't allowed to call
     62   // gtk_settings_get_default().
     63   std::string gtk_theme_name_;
     64 
     65  private:
     66   MimeUtilConstants()
     67       : kUpdateInterval(5),
     68         icon_dirs_(NULL),
     69         icon_themes_(NULL),
     70         last_check_time_(0) {
     71     icon_formats_.push_back(".png");
     72     icon_formats_.push_back(".svg");
     73     icon_formats_.push_back(".xpm");
     74 
     75     for (size_t i = 0; i < kDefaultThemeNum; ++i)
     76       default_themes_[i] = NULL;
     77   }
     78   ~MimeUtilConstants();
     79 
     80   friend struct DefaultSingletonTraits<MimeUtilConstants>;
     81 
     82   DISALLOW_COPY_AND_ASSIGN(MimeUtilConstants);
     83 };
     84 
     85 // IconTheme represents an icon theme as defined by the xdg icon theme spec.
     86 // Example themes on GNOME include 'Human' and 'Mist'.
     87 // Example themes on KDE include 'crystalsvg' and 'kdeclassic'.
     88 class IconTheme {
     89  public:
     90   // A theme consists of multiple sub-directories, like '32x32' and 'scalable'.
     91   class SubDirInfo {
     92    public:
     93     // See spec for details.
     94     enum Type {
     95       Fixed,
     96       Scalable,
     97       Threshold
     98     };
     99     SubDirInfo()
    100         : size(0),
    101           type(Threshold),
    102           max_size(0),
    103           min_size(0),
    104           threshold(2) {
    105     }
    106     size_t size;  // Nominal size of the icons in this directory.
    107     Type type;  // Type of the icon size.
    108     size_t max_size;  // Maximum size that the icons can be scaled to.
    109     size_t min_size;  // Minimum size that the icons can be scaled to.
    110     size_t threshold;  // Maximum difference from desired size. 2 by default.
    111   };
    112 
    113   explicit IconTheme(const std::string& name);
    114 
    115   ~IconTheme() {
    116     delete[] info_array_;
    117   }
    118 
    119   // Returns the path to an icon with the name |icon_name| and a size of |size|
    120   // pixels. If the icon does not exist, but |inherits| is true, then look for
    121   // the icon in the parent theme.
    122   FilePath GetIconPath(const std::string& icon_name, int size, bool inherits);
    123 
    124   // Load a theme with the name |theme_name| into memory. Returns null if theme
    125   // is invalid.
    126   static IconTheme* LoadTheme(const std::string& theme_name);
    127 
    128  private:
    129   // Returns the path to an icon with the name |icon_name| in |subdir|.
    130   FilePath GetIconPathUnderSubdir(const std::string& icon_name,
    131                                   const std::string& subdir);
    132 
    133   // Whether the theme loaded properly.
    134   bool IsValid() {
    135     return index_theme_loaded_;
    136   }
    137 
    138   // Read and parse |file| which is usually named 'index.theme' per theme spec.
    139   bool LoadIndexTheme(const FilePath& file);
    140 
    141   // Checks to see if the icons in |info| matches |size| (in pixels). Returns
    142   // 0 if they match, or the size difference in pixels.
    143   size_t MatchesSize(SubDirInfo* info, size_t size);
    144 
    145   // Yet another function to read a line.
    146   std::string ReadLine(FILE* fp);
    147 
    148   // Set directories to search for icons to the comma-separated list |dirs|.
    149   bool SetDirectories(const std::string& dirs);
    150 
    151   bool index_theme_loaded_;  // True if an instance is properly loaded.
    152   // store the scattered directories of this theme.
    153   std::list<FilePath> dirs_;
    154 
    155   // store the subdirs of this theme and array index of |info_array_|.
    156   std::map<std::string, int> subdirs_;
    157   SubDirInfo* info_array_;  // List of sub-directories.
    158   std::string inherits_;  // Name of the theme this one inherits from.
    159 };
    160 
    161 IconTheme::IconTheme(const std::string& name)
    162   : index_theme_loaded_(false),
    163     info_array_(NULL) {
    164   base::ThreadRestrictions::AssertIOAllowed();
    165   // Iterate on all icon directories to find directories of the specified
    166   // theme and load the first encountered index.theme.
    167   std::map<FilePath, int>::iterator iter;
    168   FilePath theme_path;
    169   std::map<FilePath, int>* icon_dirs =
    170       MimeUtilConstants::GetInstance()->icon_dirs_;
    171   for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
    172     theme_path = iter->first.Append(name);
    173     if (!file_util::DirectoryExists(theme_path))
    174       continue;
    175     FilePath theme_index = theme_path.Append("index.theme");
    176     if (!index_theme_loaded_ && file_util::PathExists(theme_index)) {
    177       if (!LoadIndexTheme(theme_index))
    178         return;
    179       index_theme_loaded_ = true;
    180     }
    181     dirs_.push_back(theme_path);
    182   }
    183 }
    184 
    185 FilePath IconTheme::GetIconPath(const std::string& icon_name, int size,
    186                                 bool inherits) {
    187   std::map<std::string, int>::iterator subdir_iter;
    188   FilePath icon_path;
    189 
    190   for (subdir_iter = subdirs_.begin();
    191        subdir_iter != subdirs_.end();
    192        ++subdir_iter) {
    193     SubDirInfo* info = &info_array_[subdir_iter->second];
    194     if (MatchesSize(info, size) == 0) {
    195       icon_path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
    196       if (!icon_path.empty())
    197         return icon_path;
    198     }
    199   }
    200   // Now looking for the mostly matched.
    201   int min_delta_seen = 9999;
    202 
    203   for (subdir_iter = subdirs_.begin();
    204        subdir_iter != subdirs_.end();
    205        ++subdir_iter) {
    206     SubDirInfo* info = &info_array_[subdir_iter->second];
    207     int delta = abs(MatchesSize(info, size));
    208     if (delta < min_delta_seen) {
    209       FilePath path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
    210       if (!path.empty()) {
    211         min_delta_seen = delta;
    212         icon_path = path;
    213       }
    214     }
    215   }
    216 
    217   if (!icon_path.empty() || !inherits || inherits_ == "")
    218     return icon_path;
    219 
    220   IconTheme* theme = LoadTheme(inherits_);
    221   // Inheriting from itself means the theme is buggy but we shouldn't crash.
    222   if (theme && theme != this)
    223     return theme->GetIconPath(icon_name, size, inherits);
    224   else
    225     return FilePath();
    226 }
    227 
    228 IconTheme* IconTheme::LoadTheme(const std::string& theme_name) {
    229   scoped_ptr<IconTheme> theme;
    230   std::map<std::string, IconTheme*>* icon_themes =
    231       MimeUtilConstants::GetInstance()->icon_themes_;
    232   if (icon_themes->find(theme_name) != icon_themes->end()) {
    233     theme.reset((*icon_themes)[theme_name]);
    234   } else {
    235     theme.reset(new IconTheme(theme_name));
    236     if (!theme->IsValid())
    237       theme.reset();
    238     (*icon_themes)[theme_name] = theme.get();
    239   }
    240   return theme.release();
    241 }
    242 
    243 FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name,
    244                                            const std::string& subdir) {
    245   FilePath icon_path;
    246   std::list<FilePath>::iterator dir_iter;
    247   std::vector<std::string>* icon_formats =
    248       &MimeUtilConstants::GetInstance()->icon_formats_;
    249   for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) {
    250     for (size_t i = 0; i < icon_formats->size(); ++i) {
    251       icon_path = dir_iter->Append(subdir);
    252       icon_path = icon_path.Append(icon_name + (*icon_formats)[i]);
    253       if (file_util::PathExists(icon_path))
    254         return icon_path;
    255     }
    256   }
    257   return FilePath();
    258 }
    259 
    260 bool IconTheme::LoadIndexTheme(const FilePath& file) {
    261   FILE* fp = file_util::OpenFile(file, "r");
    262   SubDirInfo* current_info = NULL;
    263   if (!fp)
    264     return false;
    265 
    266   // Read entries.
    267   while (!feof(fp) && !ferror(fp)) {
    268     std::string buf = ReadLine(fp);
    269     if (buf == "")
    270       break;
    271 
    272     std::string entry;
    273     TrimWhitespaceASCII(buf, TRIM_ALL, &entry);
    274     if (entry.length() == 0 || entry[0] == '#') {
    275       // Blank line or Comment.
    276       continue;
    277     } else if (entry[0] == '[' && info_array_) {
    278       current_info = NULL;
    279       std::string subdir = entry.substr(1, entry.length() - 2);
    280       if (subdirs_.find(subdir) != subdirs_.end())
    281         current_info = &info_array_[subdirs_[subdir]];
    282     }
    283 
    284     std::string key, value;
    285     std::vector<std::string> r;
    286     base::SplitStringDontTrim(entry, '=', &r);
    287     if (r.size() < 2)
    288       continue;
    289 
    290     TrimWhitespaceASCII(r[0], TRIM_ALL, &key);
    291     for (size_t i = 1; i < r.size(); i++)
    292       value.append(r[i]);
    293     TrimWhitespaceASCII(value, TRIM_ALL, &value);
    294 
    295     if (current_info) {
    296       if (key == "Size") {
    297         current_info->size = atoi(value.c_str());
    298       } else if (key == "Type") {
    299         if (value == "Fixed")
    300           current_info->type = SubDirInfo::Fixed;
    301         else if (value == "Scalable")
    302           current_info->type = SubDirInfo::Scalable;
    303         else if (value == "Threshold")
    304           current_info->type = SubDirInfo::Threshold;
    305       } else if (key == "MaxSize") {
    306         current_info->max_size = atoi(value.c_str());
    307       } else if (key == "MinSize") {
    308         current_info->min_size = atoi(value.c_str());
    309       } else if (key == "Threshold") {
    310         current_info->threshold = atoi(value.c_str());
    311       }
    312     } else {
    313       if (key.compare("Directories") == 0 && !info_array_) {
    314         if (!SetDirectories(value)) break;
    315       } else if (key.compare("Inherits") == 0) {
    316         if (value != "hicolor")
    317           inherits_ = value;
    318       }
    319     }
    320   }
    321 
    322   file_util::CloseFile(fp);
    323   return info_array_ != NULL;
    324 }
    325 
    326 size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) {
    327   if (info->type == SubDirInfo::Fixed) {
    328     return size - info->size;
    329   } else if (info->type == SubDirInfo::Scalable) {
    330     if (size >= info->min_size && size <= info->max_size) {
    331       return 0;
    332     } else {
    333       return abs(size - info->min_size) < abs(size - info->max_size) ?
    334           (size - info->min_size) : (size - info->max_size);
    335     }
    336   } else {
    337     if (size >= info->size - info->threshold &&
    338         size <= info->size + info->threshold) {
    339       return 0;
    340     } else {
    341       return abs(size - info->size - info->threshold) <
    342           abs(size - info->size + info->threshold)
    343           ? size - info->size - info->threshold
    344           : size - info->size + info->threshold;
    345     }
    346   }
    347 }
    348 
    349 std::string IconTheme::ReadLine(FILE* fp) {
    350   if (!fp)
    351     return "";
    352 
    353   std::string result = "";
    354   const size_t kBufferSize = 100;
    355   char buffer[kBufferSize];
    356   while ((fgets(buffer, kBufferSize - 1, fp)) != NULL) {
    357     result += buffer;
    358     size_t len = result.length();
    359     if (len == 0)
    360       break;
    361     char end = result[len - 1];
    362     if (end == '\n' || end == '\0')
    363       break;
    364   }
    365 
    366   return result;
    367 }
    368 
    369 bool IconTheme::SetDirectories(const std::string& dirs) {
    370   int num = 0;
    371   std::string::size_type pos = 0, epos;
    372   std::string dir;
    373   while ((epos = dirs.find(',', pos)) != std::string::npos) {
    374     TrimWhitespaceASCII(dirs.substr(pos, epos - pos), TRIM_ALL, &dir);
    375     if (dir.length() == 0) {
    376       LOG(WARNING) << "Invalid index.theme: blank subdir";
    377       return false;
    378     }
    379     subdirs_[dir] = num++;
    380     pos = epos + 1;
    381   }
    382   TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir);
    383   if (dir.length() == 0) {
    384     LOG(WARNING) << "Invalid index.theme: blank subdir";
    385     return false;
    386   }
    387   subdirs_[dir] = num++;
    388   info_array_ = new SubDirInfo[num];
    389   return true;
    390 }
    391 
    392 // Make sure |dir| exists and add it to the list of icon directories.
    393 void TryAddIconDir(const FilePath& dir) {
    394   if (!file_util::DirectoryExists(dir))
    395     return;
    396   (*MimeUtilConstants::GetInstance()->icon_dirs_)[dir] = 0;
    397 }
    398 
    399 // For a xdg directory |dir|, add the appropriate icon sub-directories.
    400 void AddXDGDataDir(const FilePath& dir) {
    401   if (!file_util::DirectoryExists(dir))
    402     return;
    403   TryAddIconDir(dir.Append("icons"));
    404   TryAddIconDir(dir.Append("pixmaps"));
    405 }
    406 
    407 // Add all the xdg icon directories.
    408 void InitIconDir() {
    409   MimeUtilConstants::GetInstance()->icon_dirs_->clear();
    410   FilePath home = file_util::GetHomeDir();
    411   if (!home.empty()) {
    412       FilePath legacy_data_dir(home);
    413       legacy_data_dir = legacy_data_dir.AppendASCII(".icons");
    414       if (file_util::DirectoryExists(legacy_data_dir))
    415         TryAddIconDir(legacy_data_dir);
    416   }
    417   const char* env = getenv("XDG_DATA_HOME");
    418   if (env) {
    419     AddXDGDataDir(FilePath(env));
    420   } else if (!home.empty()) {
    421     FilePath local_data_dir(home);
    422     local_data_dir = local_data_dir.AppendASCII(".local");
    423     local_data_dir = local_data_dir.AppendASCII("share");
    424     AddXDGDataDir(local_data_dir);
    425   }
    426 
    427   env = getenv("XDG_DATA_DIRS");
    428   if (!env) {
    429     AddXDGDataDir(FilePath("/usr/local/share"));
    430     AddXDGDataDir(FilePath("/usr/share"));
    431   } else {
    432     std::string xdg_data_dirs = env;
    433     std::string::size_type pos = 0, epos;
    434     while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) {
    435       AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos)));
    436       pos = epos + 1;
    437     }
    438     AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos)));
    439   }
    440 }
    441 
    442 // Per xdg theme spec, we should check the icon directories every so often for
    443 // newly added icons. This isn't quite right.
    444 void EnsureUpdated() {
    445   struct timeval t;
    446   gettimeofday(&t, NULL);
    447   time_t now = t.tv_sec;
    448   MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
    449 
    450   if (constants->last_check_time_ == 0) {
    451     constants->icon_dirs_ = new std::map<FilePath, int>;
    452     constants->icon_themes_ = new std::map<std::string, IconTheme*>;
    453     InitIconDir();
    454     constants->last_check_time_ = now;
    455   } else {
    456     // TODO(thestig): something changed. start over. Upstream fix to Google
    457     // Gadgets for Linux.
    458     if (now > constants->last_check_time_ + constants->kUpdateInterval) {
    459     }
    460   }
    461 }
    462 
    463 // Find a fallback icon if we cannot find it in the default theme.
    464 FilePath LookupFallbackIcon(const std::string& icon_name) {
    465   FilePath icon;
    466   MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
    467   std::map<FilePath, int>::iterator iter;
    468   std::map<FilePath, int>* icon_dirs = constants->icon_dirs_;
    469   std::vector<std::string>* icon_formats = &constants->icon_formats_;
    470   for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
    471     for (size_t i = 0; i < icon_formats->size(); ++i) {
    472       icon = iter->first.Append(icon_name + (*icon_formats)[i]);
    473       if (file_util::PathExists(icon))
    474         return icon;
    475     }
    476   }
    477   return FilePath();
    478 }
    479 
    480 // Initialize the list of default themes.
    481 void InitDefaultThemes() {
    482   IconTheme** default_themes =
    483       MimeUtilConstants::GetInstance()->default_themes_;
    484 
    485   char* env = getenv("KDE_FULL_SESSION");
    486   if (env) {
    487     // KDE
    488     std::string kde_default_theme;
    489     std::string kde_fallback_theme;
    490 
    491     // TODO(thestig): Figure out how to get the current icon theme on KDE.
    492     // Setting stored in ~/.kde/share/config/kdeglobals under Icons -> Theme.
    493     default_themes[0] = NULL;
    494 
    495     // Try some reasonable defaults for KDE.
    496     env = getenv("KDE_SESSION_VERSION");
    497     if (!env || env[0] != '4') {
    498       // KDE 3
    499       kde_default_theme = "default.kde";
    500       kde_fallback_theme = "crystalsvg";
    501     } else {
    502       // KDE 4
    503       kde_default_theme = "default.kde4";
    504       kde_fallback_theme = "oxygen";
    505     }
    506     default_themes[1] = IconTheme::LoadTheme(kde_default_theme);
    507     default_themes[2] = IconTheme::LoadTheme(kde_fallback_theme);
    508   } else {
    509     // Assume it's Gnome and use GTK to figure out the theme.
    510     default_themes[1] = IconTheme::LoadTheme(
    511         MimeUtilConstants::GetInstance()->gtk_theme_name_);
    512     default_themes[2] = IconTheme::LoadTheme("gnome");
    513   }
    514   // hicolor needs to be last per icon theme spec.
    515   default_themes[3] = IconTheme::LoadTheme("hicolor");
    516 
    517   for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
    518     if (default_themes[i] == NULL)
    519       continue;
    520     // NULL out duplicate pointers.
    521     for (size_t j = i + 1; j < MimeUtilConstants::kDefaultThemeNum; j++) {
    522       if (default_themes[j] == default_themes[i])
    523         default_themes[j] = NULL;
    524     }
    525   }
    526 }
    527 
    528 // Try to find an icon with the name |icon_name| that's |size| pixels.
    529 FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) {
    530   EnsureUpdated();
    531   MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
    532   std::map<std::string, IconTheme*>* icon_themes = constants->icon_themes_;
    533   if (icon_themes->empty())
    534     InitDefaultThemes();
    535 
    536   FilePath icon_path;
    537   IconTheme** default_themes = constants->default_themes_;
    538   for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
    539     if (default_themes[i]) {
    540       icon_path = default_themes[i]->GetIconPath(icon_name, size, true);
    541       if (!icon_path.empty())
    542         return icon_path;
    543     }
    544   }
    545   return LookupFallbackIcon(icon_name);
    546 }
    547 
    548 MimeUtilConstants::~MimeUtilConstants() {
    549   delete icon_dirs_;
    550   delete icon_themes_;
    551   for (size_t i = 0; i < kDefaultThemeNum; i++)
    552     delete default_themes_[i];
    553 }
    554 
    555 }  // namespace
    556 
    557 namespace mime_util {
    558 
    559 std::string GetFileMimeType(const FilePath& filepath) {
    560   base::ThreadRestrictions::AssertIOAllowed();
    561   base::AutoLock scoped_lock(g_mime_util_xdg_lock);
    562   return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str());
    563 }
    564 
    565 std::string GetDataMimeType(const std::string& data) {
    566   base::ThreadRestrictions::AssertIOAllowed();
    567   base::AutoLock scoped_lock(g_mime_util_xdg_lock);
    568   return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL);
    569 }
    570 
    571 void DetectGtkTheme() {
    572   // If the theme name is already loaded, do nothing. Chrome doesn't respond
    573   // to changes in the system theme, so we never need to set this more than
    574   // once.
    575   if (!MimeUtilConstants::GetInstance()->gtk_theme_name_.empty())
    576     return;
    577 
    578   // We should only be called on the UI thread.
    579   DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
    580 
    581   gchar* gtk_theme_name;
    582   g_object_get(gtk_settings_get_default(),
    583                "gtk-icon-theme-name",
    584                &gtk_theme_name, NULL);
    585   MimeUtilConstants::GetInstance()->gtk_theme_name_.assign(gtk_theme_name);
    586   g_free(gtk_theme_name);
    587 }
    588 
    589 FilePath GetMimeIcon(const std::string& mime_type, size_t size) {
    590   base::ThreadRestrictions::AssertIOAllowed();
    591   std::vector<std::string> icon_names;
    592   std::string icon_name;
    593   FilePath icon_file;
    594 
    595   {
    596     base::AutoLock scoped_lock(g_mime_util_xdg_lock);
    597     const char *icon = xdg_mime_get_icon(mime_type.c_str());
    598     icon_name = std::string(icon ? icon : "");
    599   }
    600 
    601   if (icon_name.length())
    602     icon_names.push_back(icon_name);
    603 
    604   // For text/plain, try text-plain.
    605   icon_name = mime_type;
    606   for (size_t i = icon_name.find('/', 0); i != std::string::npos;
    607        i = icon_name.find('/', i + 1)) {
    608     icon_name[i] = '-';
    609   }
    610   icon_names.push_back(icon_name);
    611   // Also try gnome-mime-text-plain.
    612   icon_names.push_back("gnome-mime-" + icon_name);
    613 
    614   // Try "deb" for "application/x-deb" in KDE 3.
    615   icon_name = mime_type.substr(mime_type.find("/x-") + 3);
    616   icon_names.push_back(icon_name);
    617 
    618   // Try generic name like text-x-generic.
    619   icon_name = mime_type.substr(0, mime_type.find('/')) + "-x-generic";
    620   icon_names.push_back(icon_name);
    621 
    622   // Last resort
    623   icon_names.push_back("unknown");
    624 
    625   for (size_t i = 0; i < icon_names.size(); i++) {
    626     if (icon_names[i][0] == '/') {
    627       icon_file = FilePath(icon_names[i]);
    628       if (file_util::PathExists(icon_file))
    629         return icon_file;
    630     } else {
    631       icon_file = LookupIconInDefaultTheme(icon_names[i], size);
    632       if (!icon_file.empty())
    633         return icon_file;
    634     }
    635   }
    636   return FilePath();
    637 }
    638 
    639 }  // namespace mime_util
    640