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