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