Chromium Code Reviews| 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 <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 #include <sys/time.h> | 8 #include <sys/time.h> |
| 9 #include <time.h> | 9 #include <time.h> |
| 10 | 10 |
| 11 #include <cstdlib> | 11 #include <cstdlib> |
| 12 #include <list> | 12 #include <list> |
| 13 #include <map> | 13 #include <map> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/file_util.h" | 16 #include "base/file_util.h" |
| 17 #include "base/lazy_instance.h" | |
| 17 #include "base/logging.h" | 18 #include "base/logging.h" |
| 18 #include "base/memory/scoped_ptr.h" | 19 #include "base/memory/scoped_ptr.h" |
| 19 #include "base/memory/singleton.h" | 20 #include "base/memory/singleton.h" |
| 20 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
| 21 #include "base/string_split.h" | 22 #include "base/string_split.h" |
| 22 #include "base/string_util.h" | 23 #include "base/string_util.h" |
| 23 #include "base/synchronization/lock.h" | 24 #include "base/synchronization/lock.h" |
| 24 #include "base/third_party/xdg_mime/xdgmime.h" | 25 #include "base/third_party/xdg_mime/xdgmime.h" |
| 25 #include "base/threading/thread_restrictions.h" | 26 #include "base/threading/thread_restrictions.h" |
| 26 | 27 |
| 27 namespace { | 28 namespace { |
| 28 | 29 |
| 29 // None of the XDG stuff is thread-safe, so serialize all accesss under | 30 // None of the XDG stuff is thread-safe, so serialize all access under |
| 30 // this lock. | 31 // this lock. |
| 31 base::Lock g_mime_util_xdg_lock; | 32 static base::LazyInstance<base::Lock> |
|
willchan no longer on Chromium
2011/10/28 21:42:25
I wonder if you should use LeakyLazyInstance. Not
Lei Zhang
2011/10/28 21:54:02
Would you say that's true of all locks? There's 7
| |
| 33 g_mime_util_xdg_lock(base::LINKER_INITIALIZED); | |
| 32 | 34 |
| 33 class IconTheme; | 35 class IconTheme; |
| 34 | 36 |
| 35 class MimeUtilConstants { | 37 class MimeUtilConstants { |
| 36 public: | 38 public: |
| 39 typedef std::map<std::string, IconTheme*> IconThemeMap; | |
| 40 typedef std::map<FilePath, int> IconDirMtimeMap; | |
| 41 typedef std::vector<std::string> IconFormats; | |
| 42 | |
| 43 // In seconds, specified by icon theme specs. | |
| 44 static const int kUpdateInterval = 5; | |
| 45 | |
| 46 static const size_t kDefaultThemeNum = 4; | |
| 47 | |
| 37 static MimeUtilConstants* GetInstance() { | 48 static MimeUtilConstants* GetInstance() { |
| 38 return Singleton<MimeUtilConstants>::get(); | 49 return Singleton<MimeUtilConstants>::get(); |
| 39 } | 50 } |
| 40 | 51 |
| 41 // In seconds, specified by icon theme specs. | |
| 42 const int kUpdateInterval; | |
| 43 | |
| 44 // Store icon directories and their mtimes. | 52 // Store icon directories and their mtimes. |
| 45 std::map<FilePath, int>* icon_dirs_; | 53 IconDirMtimeMap* icon_dirs_; |
| 46 | 54 |
| 47 // Store icon formats. | 55 // Store icon formats. |
| 48 std::vector<std::string> icon_formats_; | 56 IconFormats icon_formats_; |
| 49 | 57 |
| 50 // Store loaded icon_theme. | 58 // Store loaded icon_theme. |
| 51 std::map<std::string, IconTheme*>* icon_themes_; | 59 IconThemeMap* icon_themes_; |
| 52 | |
| 53 static const size_t kDefaultThemeNum = 4; | |
| 54 | 60 |
| 55 // The default theme. | 61 // The default theme. |
| 56 IconTheme* default_themes_[kDefaultThemeNum]; | 62 IconTheme* default_themes_[kDefaultThemeNum]; |
| 57 | 63 |
| 58 time_t last_check_time_; | 64 time_t last_check_time_; |
| 59 | 65 |
| 60 // This is set by DetectGtkTheme(). We cache it so that we can access the | 66 // This is set by DetectGtkTheme(). We cache it so that we can access the |
| 61 // theme name from threads that aren't allowed to call | 67 // theme name from threads that aren't allowed to call |
| 62 // gtk_settings_get_default(). | 68 // gtk_settings_get_default(). |
| 63 std::string gtk_theme_name_; | 69 std::string gtk_theme_name_; |
| 64 | 70 |
| 65 private: | 71 private: |
| 66 MimeUtilConstants() | 72 MimeUtilConstants() |
| 67 : kUpdateInterval(5), | 73 : icon_dirs_(NULL), |
| 68 icon_dirs_(NULL), | |
| 69 icon_themes_(NULL), | 74 icon_themes_(NULL), |
| 70 last_check_time_(0) { | 75 last_check_time_(0) { |
| 71 icon_formats_.push_back(".png"); | 76 icon_formats_.push_back(".png"); |
| 72 icon_formats_.push_back(".svg"); | 77 icon_formats_.push_back(".svg"); |
| 73 icon_formats_.push_back(".xpm"); | 78 icon_formats_.push_back(".xpm"); |
| 74 | 79 |
| 75 for (size_t i = 0; i < kDefaultThemeNum; ++i) | 80 for (size_t i = 0; i < kDefaultThemeNum; ++i) |
| 76 default_themes_[i] = NULL; | 81 default_themes_[i] = NULL; |
| 77 } | 82 } |
| 78 ~MimeUtilConstants(); | 83 ~MimeUtilConstants(); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 SubDirInfo* info_array_; // List of sub-directories. | 162 SubDirInfo* info_array_; // List of sub-directories. |
| 158 std::string inherits_; // Name of the theme this one inherits from. | 163 std::string inherits_; // Name of the theme this one inherits from. |
| 159 }; | 164 }; |
| 160 | 165 |
| 161 IconTheme::IconTheme(const std::string& name) | 166 IconTheme::IconTheme(const std::string& name) |
| 162 : index_theme_loaded_(false), | 167 : index_theme_loaded_(false), |
| 163 info_array_(NULL) { | 168 info_array_(NULL) { |
| 164 base::ThreadRestrictions::AssertIOAllowed(); | 169 base::ThreadRestrictions::AssertIOAllowed(); |
| 165 // Iterate on all icon directories to find directories of the specified | 170 // Iterate on all icon directories to find directories of the specified |
| 166 // theme and load the first encountered index.theme. | 171 // theme and load the first encountered index.theme. |
| 167 std::map<FilePath, int>::iterator iter; | 172 MimeUtilConstants::IconDirMtimeMap::iterator iter; |
| 168 FilePath theme_path; | 173 FilePath theme_path; |
| 169 std::map<FilePath, int>* icon_dirs = | 174 MimeUtilConstants::IconDirMtimeMap* icon_dirs = |
| 170 MimeUtilConstants::GetInstance()->icon_dirs_; | 175 MimeUtilConstants::GetInstance()->icon_dirs_; |
| 171 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { | 176 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { |
| 172 theme_path = iter->first.Append(name); | 177 theme_path = iter->first.Append(name); |
| 173 if (!file_util::DirectoryExists(theme_path)) | 178 if (!file_util::DirectoryExists(theme_path)) |
| 174 continue; | 179 continue; |
| 175 FilePath theme_index = theme_path.Append("index.theme"); | 180 FilePath theme_index = theme_path.Append("index.theme"); |
| 176 if (!index_theme_loaded_ && file_util::PathExists(theme_index)) { | 181 if (!index_theme_loaded_ && file_util::PathExists(theme_index)) { |
| 177 if (!LoadIndexTheme(theme_index)) | 182 if (!LoadIndexTheme(theme_index)) |
| 178 return; | 183 return; |
| 179 index_theme_loaded_ = true; | 184 index_theme_loaded_ = true; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 IconTheme* theme = LoadTheme(inherits_); | 225 IconTheme* theme = LoadTheme(inherits_); |
| 221 // Inheriting from itself means the theme is buggy but we shouldn't crash. | 226 // Inheriting from itself means the theme is buggy but we shouldn't crash. |
| 222 if (theme && theme != this) | 227 if (theme && theme != this) |
| 223 return theme->GetIconPath(icon_name, size, inherits); | 228 return theme->GetIconPath(icon_name, size, inherits); |
| 224 else | 229 else |
| 225 return FilePath(); | 230 return FilePath(); |
| 226 } | 231 } |
| 227 | 232 |
| 228 IconTheme* IconTheme::LoadTheme(const std::string& theme_name) { | 233 IconTheme* IconTheme::LoadTheme(const std::string& theme_name) { |
| 229 scoped_ptr<IconTheme> theme; | 234 scoped_ptr<IconTheme> theme; |
| 230 std::map<std::string, IconTheme*>* icon_themes = | 235 MimeUtilConstants::IconThemeMap* icon_themes = |
| 231 MimeUtilConstants::GetInstance()->icon_themes_; | 236 MimeUtilConstants::GetInstance()->icon_themes_; |
| 232 if (icon_themes->find(theme_name) != icon_themes->end()) { | 237 if (icon_themes->find(theme_name) != icon_themes->end()) { |
| 233 theme.reset((*icon_themes)[theme_name]); | 238 theme.reset((*icon_themes)[theme_name]); |
| 234 } else { | 239 } else { |
| 235 theme.reset(new IconTheme(theme_name)); | 240 theme.reset(new IconTheme(theme_name)); |
| 236 if (!theme->IsValid()) | 241 if (!theme->IsValid()) |
| 237 theme.reset(); | 242 theme.reset(); |
| 238 (*icon_themes)[theme_name] = theme.get(); | 243 (*icon_themes)[theme_name] = theme.get(); |
| 239 } | 244 } |
| 240 return theme.release(); | 245 return theme.release(); |
| 241 } | 246 } |
| 242 | 247 |
| 243 FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name, | 248 FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name, |
| 244 const std::string& subdir) { | 249 const std::string& subdir) { |
| 245 FilePath icon_path; | 250 FilePath icon_path; |
| 246 std::list<FilePath>::iterator dir_iter; | 251 std::list<FilePath>::iterator dir_iter; |
| 247 std::vector<std::string>* icon_formats = | 252 MimeUtilConstants::IconFormats* icon_formats = |
| 248 &MimeUtilConstants::GetInstance()->icon_formats_; | 253 &MimeUtilConstants::GetInstance()->icon_formats_; |
| 249 for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) { | 254 for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) { |
| 250 for (size_t i = 0; i < icon_formats->size(); ++i) { | 255 for (size_t i = 0; i < icon_formats->size(); ++i) { |
| 251 icon_path = dir_iter->Append(subdir); | 256 icon_path = dir_iter->Append(subdir); |
| 252 icon_path = icon_path.Append(icon_name + (*icon_formats)[i]); | 257 icon_path = icon_path.Append(icon_name + (*icon_formats)[i]); |
| 253 if (file_util::PathExists(icon_path)) | 258 if (file_util::PathExists(icon_path)) |
| 254 return icon_path; | 259 return icon_path; |
| 255 } | 260 } |
| 256 } | 261 } |
| 257 return FilePath(); | 262 return FilePath(); |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 439 | 444 |
| 440 // Per xdg theme spec, we should check the icon directories every so often for | 445 // Per xdg theme spec, we should check the icon directories every so often for |
| 441 // newly added icons. This isn't quite right. | 446 // newly added icons. This isn't quite right. |
| 442 void EnsureUpdated() { | 447 void EnsureUpdated() { |
| 443 struct timeval t; | 448 struct timeval t; |
| 444 gettimeofday(&t, NULL); | 449 gettimeofday(&t, NULL); |
| 445 time_t now = t.tv_sec; | 450 time_t now = t.tv_sec; |
| 446 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); | 451 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); |
| 447 | 452 |
| 448 if (constants->last_check_time_ == 0) { | 453 if (constants->last_check_time_ == 0) { |
| 449 constants->icon_dirs_ = new std::map<FilePath, int>; | 454 constants->icon_dirs_ = new MimeUtilConstants::IconDirMtimeMap; |
| 450 constants->icon_themes_ = new std::map<std::string, IconTheme*>; | 455 constants->icon_themes_ = new MimeUtilConstants::IconThemeMap; |
| 451 InitIconDir(); | 456 InitIconDir(); |
| 452 constants->last_check_time_ = now; | 457 constants->last_check_time_ = now; |
| 453 } else { | 458 } else { |
| 454 // TODO(thestig): something changed. start over. Upstream fix to Google | 459 // TODO(thestig): something changed. start over. Upstream fix to Google |
| 455 // Gadgets for Linux. | 460 // Gadgets for Linux. |
| 456 if (now > constants->last_check_time_ + constants->kUpdateInterval) { | 461 if (now > constants->last_check_time_ + constants->kUpdateInterval) { |
| 457 } | 462 } |
| 458 } | 463 } |
| 459 } | 464 } |
| 460 | 465 |
| 461 // Find a fallback icon if we cannot find it in the default theme. | 466 // Find a fallback icon if we cannot find it in the default theme. |
| 462 FilePath LookupFallbackIcon(const std::string& icon_name) { | 467 FilePath LookupFallbackIcon(const std::string& icon_name) { |
| 463 FilePath icon; | 468 FilePath icon; |
| 464 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); | 469 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); |
| 465 std::map<FilePath, int>::iterator iter; | 470 MimeUtilConstants::IconDirMtimeMap::iterator iter; |
| 466 std::map<FilePath, int>* icon_dirs = constants->icon_dirs_; | 471 MimeUtilConstants::IconDirMtimeMap* icon_dirs = constants->icon_dirs_; |
| 467 std::vector<std::string>* icon_formats = &constants->icon_formats_; | 472 MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_; |
| 468 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { | 473 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { |
| 469 for (size_t i = 0; i < icon_formats->size(); ++i) { | 474 for (size_t i = 0; i < icon_formats->size(); ++i) { |
| 470 icon = iter->first.Append(icon_name + (*icon_formats)[i]); | 475 icon = iter->first.Append(icon_name + (*icon_formats)[i]); |
| 471 if (file_util::PathExists(icon)) | 476 if (file_util::PathExists(icon)) |
| 472 return icon; | 477 return icon; |
| 473 } | 478 } |
| 474 } | 479 } |
| 475 return FilePath(); | 480 return FilePath(); |
| 476 } | 481 } |
| 477 | 482 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 520 if (default_themes[j] == default_themes[i]) | 525 if (default_themes[j] == default_themes[i]) |
| 521 default_themes[j] = NULL; | 526 default_themes[j] = NULL; |
| 522 } | 527 } |
| 523 } | 528 } |
| 524 } | 529 } |
| 525 | 530 |
| 526 // Try to find an icon with the name |icon_name| that's |size| pixels. | 531 // Try to find an icon with the name |icon_name| that's |size| pixels. |
| 527 FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) { | 532 FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) { |
| 528 EnsureUpdated(); | 533 EnsureUpdated(); |
| 529 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); | 534 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); |
| 530 std::map<std::string, IconTheme*>* icon_themes = constants->icon_themes_; | 535 MimeUtilConstants::IconThemeMap* icon_themes = constants->icon_themes_; |
| 531 if (icon_themes->empty()) | 536 if (icon_themes->empty()) |
| 532 InitDefaultThemes(); | 537 InitDefaultThemes(); |
| 533 | 538 |
| 534 FilePath icon_path; | 539 FilePath icon_path; |
| 535 IconTheme** default_themes = constants->default_themes_; | 540 IconTheme** default_themes = constants->default_themes_; |
| 536 for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) { | 541 for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) { |
| 537 if (default_themes[i]) { | 542 if (default_themes[i]) { |
| 538 icon_path = default_themes[i]->GetIconPath(icon_name, size, true); | 543 icon_path = default_themes[i]->GetIconPath(icon_name, size, true); |
| 539 if (!icon_path.empty()) | 544 if (!icon_path.empty()) |
| 540 return icon_path; | 545 return icon_path; |
| 541 } | 546 } |
| 542 } | 547 } |
| 543 return LookupFallbackIcon(icon_name); | 548 return LookupFallbackIcon(icon_name); |
| 544 } | 549 } |
| 545 | 550 |
| 546 MimeUtilConstants::~MimeUtilConstants() { | 551 MimeUtilConstants::~MimeUtilConstants() { |
| 547 delete icon_dirs_; | 552 delete icon_dirs_; |
| 548 delete icon_themes_; | 553 delete icon_themes_; |
| 549 for (size_t i = 0; i < kDefaultThemeNum; i++) | 554 for (size_t i = 0; i < kDefaultThemeNum; i++) |
| 550 delete default_themes_[i]; | 555 delete default_themes_[i]; |
| 551 } | 556 } |
| 552 | 557 |
| 553 } // namespace | 558 } // namespace |
| 554 | 559 |
| 555 namespace mime_util { | 560 namespace mime_util { |
| 556 | 561 |
| 557 std::string GetFileMimeType(const FilePath& filepath) { | 562 std::string GetFileMimeType(const FilePath& filepath) { |
| 558 base::ThreadRestrictions::AssertIOAllowed(); | 563 base::ThreadRestrictions::AssertIOAllowed(); |
| 559 base::AutoLock scoped_lock(g_mime_util_xdg_lock); | 564 base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); |
| 560 return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str()); | 565 return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str()); |
| 561 } | 566 } |
| 562 | 567 |
| 563 std::string GetDataMimeType(const std::string& data) { | 568 std::string GetDataMimeType(const std::string& data) { |
| 564 base::ThreadRestrictions::AssertIOAllowed(); | 569 base::ThreadRestrictions::AssertIOAllowed(); |
| 565 base::AutoLock scoped_lock(g_mime_util_xdg_lock); | 570 base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); |
| 566 return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL); | 571 return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL); |
| 567 } | 572 } |
| 568 | 573 |
| 569 void DetectGtkTheme() { | 574 void DetectGtkTheme() { |
| 570 // If the theme name is already loaded, do nothing. Chrome doesn't respond | 575 // If the theme name is already loaded, do nothing. Chrome doesn't respond |
| 571 // to changes in the system theme, so we never need to set this more than | 576 // to changes in the system theme, so we never need to set this more than |
| 572 // once. | 577 // once. |
| 573 if (!MimeUtilConstants::GetInstance()->gtk_theme_name_.empty()) | 578 if (!MimeUtilConstants::GetInstance()->gtk_theme_name_.empty()) |
| 574 return; | 579 return; |
| 575 | 580 |
| 576 // We should only be called on the UI thread. | 581 // We should only be called on the UI thread. |
| 577 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); | 582 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); |
| 578 | 583 |
| 579 gchar* gtk_theme_name; | 584 gchar* gtk_theme_name; |
| 580 g_object_get(gtk_settings_get_default(), | 585 g_object_get(gtk_settings_get_default(), |
| 581 "gtk-icon-theme-name", | 586 "gtk-icon-theme-name", |
| 582 >k_theme_name, NULL); | 587 >k_theme_name, NULL); |
| 583 MimeUtilConstants::GetInstance()->gtk_theme_name_.assign(gtk_theme_name); | 588 MimeUtilConstants::GetInstance()->gtk_theme_name_.assign(gtk_theme_name); |
| 584 g_free(gtk_theme_name); | 589 g_free(gtk_theme_name); |
| 585 } | 590 } |
| 586 | 591 |
| 587 FilePath GetMimeIcon(const std::string& mime_type, size_t size) { | 592 FilePath GetMimeIcon(const std::string& mime_type, size_t size) { |
| 588 base::ThreadRestrictions::AssertIOAllowed(); | 593 base::ThreadRestrictions::AssertIOAllowed(); |
| 589 std::vector<std::string> icon_names; | 594 std::vector<std::string> icon_names; |
| 590 std::string icon_name; | 595 std::string icon_name; |
| 591 FilePath icon_file; | 596 FilePath icon_file; |
| 592 | 597 |
| 593 { | 598 { |
| 594 base::AutoLock scoped_lock(g_mime_util_xdg_lock); | 599 base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); |
| 595 const char *icon = xdg_mime_get_icon(mime_type.c_str()); | 600 const char *icon = xdg_mime_get_icon(mime_type.c_str()); |
| 596 icon_name = std::string(icon ? icon : ""); | 601 icon_name = std::string(icon ? icon : ""); |
| 597 } | 602 } |
| 598 | 603 |
| 599 if (icon_name.length()) | 604 if (icon_name.length()) |
| 600 icon_names.push_back(icon_name); | 605 icon_names.push_back(icon_name); |
| 601 | 606 |
| 602 // For text/plain, try text-plain. | 607 // For text/plain, try text-plain. |
| 603 icon_name = mime_type; | 608 icon_name = mime_type; |
| 604 for (size_t i = icon_name.find('/', 0); i != std::string::npos; | 609 for (size_t i = icon_name.find('/', 0); i != std::string::npos; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 628 } else { | 633 } else { |
| 629 icon_file = LookupIconInDefaultTheme(icon_names[i], size); | 634 icon_file = LookupIconInDefaultTheme(icon_names[i], size); |
| 630 if (!icon_file.empty()) | 635 if (!icon_file.empty()) |
| 631 return icon_file; | 636 return icon_file; |
| 632 } | 637 } |
| 633 } | 638 } |
| 634 return FilePath(); | 639 return FilePath(); |
| 635 } | 640 } |
| 636 | 641 |
| 637 } // namespace mime_util | 642 } // namespace mime_util |
| OLD | NEW |