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