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