| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/mime_util.h" | 5 #include "base/mime_util.h" |
| 6 | 6 |
| 7 #include <sys/time.h> | |
| 8 #include <time.h> | |
| 9 | 7 |
| 10 #include <cstdlib> | 8 #include <cstdlib> |
| 11 #include <list> | 9 #include <list> |
| 12 #include <map> | 10 #include <map> |
| 13 #include <vector> | 11 #include <vector> |
| 14 | 12 |
| 15 #include "base/environment.h" | 13 #include "base/environment.h" |
| 16 #include "base/file_util.h" | 14 #include "base/file_util.h" |
| 17 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
| 18 #include "base/logging.h" | 16 #include "base/logging.h" |
| 19 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 20 #include "base/memory/singleton.h" | 18 #include "base/memory/singleton.h" |
| 21 #include "base/message_loop.h" | 19 #include "base/message_loop.h" |
| 22 #include "base/nix/xdg_util.h" | 20 #include "base/nix/xdg_util.h" |
| 23 #include "base/string_split.h" | 21 #include "base/string_split.h" |
| 24 #include "base/string_util.h" | 22 #include "base/string_util.h" |
| 25 #include "base/synchronization/lock.h" | 23 #include "base/synchronization/lock.h" |
| 26 #include "base/third_party/xdg_mime/xdgmime.h" | 24 #include "base/third_party/xdg_mime/xdgmime.h" |
| 27 #include "base/threading/thread_restrictions.h" | 25 #include "base/threading/thread_restrictions.h" |
| 26 #include "base/time.h" |
| 28 | 27 |
| 29 #if defined(TOOLKIT_USES_GTK) | 28 #if defined(TOOLKIT_USES_GTK) |
| 30 #include <gtk/gtk.h> | 29 #include <gtk/gtk.h> |
| 31 #endif | 30 #endif |
| 32 | 31 |
| 33 namespace { | 32 namespace { |
| 34 | 33 |
| 35 // None of the XDG stuff is thread-safe, so serialize all access under | 34 // None of the XDG stuff is thread-safe, so serialize all access under |
| 36 // this lock. | 35 // this lock. |
| 37 static base::LazyInstance<base::Lock, | 36 static base::LazyInstance<base::Lock, |
| 38 base::LeakyLazyInstanceTraits<base::Lock> > | 37 base::LeakyLazyInstanceTraits<base::Lock> > |
| 39 g_mime_util_xdg_lock(base::LINKER_INITIALIZED); | 38 g_mime_util_xdg_lock(base::LINKER_INITIALIZED); |
| 40 | 39 |
| 41 class IconTheme; | 40 class IconTheme; |
| 42 | 41 |
| 43 class MimeUtilConstants { | 42 class MimeUtilConstants { |
| 44 public: | 43 public: |
| 45 typedef std::map<std::string, IconTheme*> IconThemeMap; | 44 typedef std::map<std::string, IconTheme*> IconThemeMap; |
| 46 typedef std::map<FilePath, int> IconDirMtimeMap; | 45 typedef std::map<FilePath, base::Time> IconDirMtimeMap; |
| 47 typedef std::vector<std::string> IconFormats; | 46 typedef std::vector<std::string> IconFormats; |
| 48 | 47 |
| 49 // In seconds, specified by icon theme specs. | 48 // Specified by XDG icon theme specs. |
| 50 static const int kUpdateInterval = 5; | 49 static const int kUpdateIntervalInSeconds = 5; |
| 51 | 50 |
| 52 static const size_t kDefaultThemeNum = 4; | 51 static const size_t kDefaultThemeNum = 4; |
| 53 | 52 |
| 54 static MimeUtilConstants* GetInstance() { | 53 static MimeUtilConstants* GetInstance() { |
| 55 return Singleton<MimeUtilConstants>::get(); | 54 return Singleton<MimeUtilConstants>::get(); |
| 56 } | 55 } |
| 57 | 56 |
| 58 // Store icon directories and their mtimes. | 57 // Store icon directories and their mtimes. |
| 59 IconDirMtimeMap icon_dirs_; | 58 IconDirMtimeMap icon_dirs_; |
| 60 | 59 |
| 61 // Store icon formats. | 60 // Store icon formats. |
| 62 IconFormats icon_formats_; | 61 IconFormats icon_formats_; |
| 63 | 62 |
| 64 // Store loaded icon_theme. | 63 // Store loaded icon_theme. |
| 65 IconThemeMap icon_themes_; | 64 IconThemeMap icon_themes_; |
| 66 | 65 |
| 67 // The default theme. | 66 // The default theme. |
| 68 IconTheme* default_themes_[kDefaultThemeNum]; | 67 IconTheme* default_themes_[kDefaultThemeNum]; |
| 69 | 68 |
| 70 time_t last_check_time_; | 69 base::TimeTicks last_check_time_; |
| 71 | 70 |
| 72 #if defined(TOOLKIT_USES_GTK) | 71 #if defined(TOOLKIT_USES_GTK) |
| 73 // This is set by DetectGtkTheme(). We cache it so that we can access the | 72 // This is set by DetectGtkTheme(). We cache it so that we can access the |
| 74 // theme name from threads that aren't allowed to call | 73 // theme name from threads that aren't allowed to call |
| 75 // gtk_settings_get_default(). | 74 // gtk_settings_get_default(). |
| 76 std::string gtk_theme_name_; | 75 std::string gtk_theme_name_; |
| 77 #endif | 76 #endif |
| 78 | 77 |
| 79 private: | 78 private: |
| 80 MimeUtilConstants() | 79 MimeUtilConstants() { |
| 81 : last_check_time_(0) { | |
| 82 icon_formats_.push_back(".png"); | 80 icon_formats_.push_back(".png"); |
| 83 icon_formats_.push_back(".svg"); | 81 icon_formats_.push_back(".svg"); |
| 84 icon_formats_.push_back(".xpm"); | 82 icon_formats_.push_back(".xpm"); |
| 85 | 83 |
| 86 for (size_t i = 0; i < kDefaultThemeNum; ++i) | 84 for (size_t i = 0; i < kDefaultThemeNum; ++i) |
| 87 default_themes_[i] = NULL; | 85 default_themes_[i] = NULL; |
| 88 } | 86 } |
| 89 ~MimeUtilConstants(); | 87 ~MimeUtilConstants(); |
| 90 | 88 |
| 91 friend struct DefaultSingletonTraits<MimeUtilConstants>; | 89 friend struct DefaultSingletonTraits<MimeUtilConstants>; |
| (...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir); | 387 TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir); |
| 390 if (dir.length() == 0) { | 388 if (dir.length() == 0) { |
| 391 DLOG(WARNING) << "Invalid index.theme: blank subdir"; | 389 DLOG(WARNING) << "Invalid index.theme: blank subdir"; |
| 392 return false; | 390 return false; |
| 393 } | 391 } |
| 394 subdirs_[dir] = num++; | 392 subdirs_[dir] = num++; |
| 395 info_array_.reset(new SubDirInfo[num]); | 393 info_array_.reset(new SubDirInfo[num]); |
| 396 return true; | 394 return true; |
| 397 } | 395 } |
| 398 | 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 |
| 399 // Make sure |dir| exists and add it to the list of icon directories. | 408 // Make sure |dir| exists and add it to the list of icon directories. |
| 400 void TryAddIconDir(const FilePath& dir) { | 409 void TryAddIconDir(const FilePath& dir) { |
| 401 if (!file_util::DirectoryExists(dir)) | 410 base::Time last_modified; |
| 411 if (!CheckDirExistsAndGetMtime(dir, &last_modified)) |
| 402 return; | 412 return; |
| 403 MimeUtilConstants::GetInstance()->icon_dirs_[dir] = 0; | 413 MimeUtilConstants::GetInstance()->icon_dirs_[dir] = last_modified; |
| 404 } | 414 } |
| 405 | 415 |
| 406 // For a xdg directory |dir|, add the appropriate icon sub-directories. | 416 // For a xdg directory |dir|, add the appropriate icon sub-directories. |
| 407 void AddXDGDataDir(const FilePath& dir) { | 417 void AddXDGDataDir(const FilePath& dir) { |
| 408 if (!file_util::DirectoryExists(dir)) | 418 if (!file_util::DirectoryExists(dir)) |
| 409 return; | 419 return; |
| 410 TryAddIconDir(dir.Append("icons")); | 420 TryAddIconDir(dir.Append("icons")); |
| 411 TryAddIconDir(dir.Append("pixmaps")); | 421 TryAddIconDir(dir.Append("pixmaps")); |
| 412 } | 422 } |
| 413 | 423 |
| 414 // Add all the xdg icon directories. | 424 // Add all the xdg icon directories. |
| 415 void InitIconDir() { | 425 void InitIconDir() { |
| 416 MimeUtilConstants::GetInstance()->icon_dirs_.clear(); | |
| 417 FilePath home = file_util::GetHomeDir(); | 426 FilePath home = file_util::GetHomeDir(); |
| 418 if (!home.empty()) { | 427 if (!home.empty()) { |
| 419 FilePath legacy_data_dir(home); | 428 FilePath legacy_data_dir(home); |
| 420 legacy_data_dir = legacy_data_dir.AppendASCII(".icons"); | 429 legacy_data_dir = legacy_data_dir.AppendASCII(".icons"); |
| 421 if (file_util::DirectoryExists(legacy_data_dir)) | 430 if (file_util::DirectoryExists(legacy_data_dir)) |
| 422 TryAddIconDir(legacy_data_dir); | 431 TryAddIconDir(legacy_data_dir); |
| 423 } | 432 } |
| 424 const char* env = getenv("XDG_DATA_HOME"); | 433 const char* env = getenv("XDG_DATA_HOME"); |
| 425 if (env) { | 434 if (env) { |
| 426 AddXDGDataDir(FilePath(env)); | 435 AddXDGDataDir(FilePath(env)); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 439 std::string xdg_data_dirs = env; | 448 std::string xdg_data_dirs = env; |
| 440 std::string::size_type pos = 0, epos; | 449 std::string::size_type pos = 0, epos; |
| 441 while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) { | 450 while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) { |
| 442 AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos))); | 451 AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos))); |
| 443 pos = epos + 1; | 452 pos = epos + 1; |
| 444 } | 453 } |
| 445 AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos))); | 454 AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos))); |
| 446 } | 455 } |
| 447 } | 456 } |
| 448 | 457 |
| 449 // Per xdg theme spec, we should check the icon directories every so often for | |
| 450 // newly added icons. This isn't quite right. | |
| 451 void EnsureUpdated() { | 458 void EnsureUpdated() { |
| 452 struct timeval t; | |
| 453 gettimeofday(&t, NULL); | |
| 454 time_t now = t.tv_sec; | |
| 455 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); | 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 } |
| 456 | 465 |
| 457 if (constants->last_check_time_ == 0) { | 466 // Per xdg theme spec, we should check the icon directories every so often |
| 458 InitIconDir(); | 467 // for newly added icons. |
| 459 constants->last_check_time_ = now; | 468 base::TimeDelta time_since_last_check = |
| 460 } else { | 469 base::TimeTicks::Now() - constants->last_check_time_; |
| 461 // TODO(thestig): something changed. start over. Upstream fix to Google | 470 if (time_since_last_check.InSeconds() > constants->kUpdateIntervalInSeconds) { |
| 462 // Gadgets for Linux. | 471 constants->last_check_time_ += time_since_last_check; |
| 463 if (now > constants->last_check_time_ + constants->kUpdateInterval) { | 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(); |
| 464 } | 489 } |
| 465 } | 490 } |
| 466 } | 491 } |
| 467 | 492 |
| 468 // Find a fallback icon if we cannot find it in the default theme. | 493 // Find a fallback icon if we cannot find it in the default theme. |
| 469 FilePath LookupFallbackIcon(const std::string& icon_name) { | 494 FilePath LookupFallbackIcon(const std::string& icon_name) { |
| 470 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); | 495 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); |
| 471 MimeUtilConstants::IconDirMtimeMap::iterator iter; | 496 MimeUtilConstants::IconDirMtimeMap::iterator iter; |
| 472 MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_; | 497 MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_; |
| 473 MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_; | 498 MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 } else { | 666 } else { |
| 642 icon_file = LookupIconInDefaultTheme(icon_names[i], size); | 667 icon_file = LookupIconInDefaultTheme(icon_names[i], size); |
| 643 if (!icon_file.empty()) | 668 if (!icon_file.empty()) |
| 644 return icon_file; | 669 return icon_file; |
| 645 } | 670 } |
| 646 } | 671 } |
| 647 return FilePath(); | 672 return FilePath(); |
| 648 } | 673 } |
| 649 | 674 |
| 650 } // namespace mime_util | 675 } // namespace mime_util |
| OLD | NEW |