Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: base/mime_util_xdg.cc

Issue 8372047: Cleanup: No need to dynamically allocate a couple class members in mime_util_xdg.cc. Also convert... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 // In seconds, specified by icon theme specs. 46 // In seconds, specified by icon theme specs.
47 static const int kUpdateInterval = 5; 47 static const int kUpdateInterval = 5;
48 48
49 static const size_t kDefaultThemeNum = 4; 49 static const size_t kDefaultThemeNum = 4;
50 50
51 static MimeUtilConstants* GetInstance() { 51 static MimeUtilConstants* GetInstance() {
52 return Singleton<MimeUtilConstants>::get(); 52 return Singleton<MimeUtilConstants>::get();
53 } 53 }
54 54
55 // Store icon directories and their mtimes. 55 // Store icon directories and their mtimes.
56 IconDirMtimeMap* icon_dirs_; 56 IconDirMtimeMap icon_dirs_;
57 57
58 // Store icon formats. 58 // Store icon formats.
59 IconFormats icon_formats_; 59 IconFormats icon_formats_;
60 60
61 // Store loaded icon_theme. 61 // Store loaded icon_theme.
62 IconThemeMap* icon_themes_; 62 IconThemeMap icon_themes_;
63 63
64 // The default theme. 64 // The default theme.
65 IconTheme* default_themes_[kDefaultThemeNum]; 65 IconTheme* default_themes_[kDefaultThemeNum];
66 66
67 time_t last_check_time_; 67 time_t last_check_time_;
68 68
69 // This is set by DetectGtkTheme(). We cache it so that we can access the 69 // This is set by DetectGtkTheme(). We cache it so that we can access the
70 // theme name from threads that aren't allowed to call 70 // theme name from threads that aren't allowed to call
71 // gtk_settings_get_default(). 71 // gtk_settings_get_default().
72 std::string gtk_theme_name_; 72 std::string gtk_theme_name_;
73 73
74 private: 74 private:
75 MimeUtilConstants() 75 MimeUtilConstants()
76 : icon_dirs_(NULL), 76 : last_check_time_(0) {
77 icon_themes_(NULL),
78 last_check_time_(0) {
79 icon_formats_.push_back(".png"); 77 icon_formats_.push_back(".png");
80 icon_formats_.push_back(".svg"); 78 icon_formats_.push_back(".svg");
81 icon_formats_.push_back(".xpm"); 79 icon_formats_.push_back(".xpm");
82 80
83 for (size_t i = 0; i < kDefaultThemeNum; ++i) 81 for (size_t i = 0; i < kDefaultThemeNum; ++i)
84 default_themes_[i] = NULL; 82 default_themes_[i] = NULL;
85 } 83 }
86 ~MimeUtilConstants(); 84 ~MimeUtilConstants();
87 85
88 friend struct DefaultSingletonTraits<MimeUtilConstants>; 86 friend struct DefaultSingletonTraits<MimeUtilConstants>;
(...skipping 24 matching lines...) Expand all
113 } 111 }
114 size_t size; // Nominal size of the icons in this directory. 112 size_t size; // Nominal size of the icons in this directory.
115 Type type; // Type of the icon size. 113 Type type; // Type of the icon size.
116 size_t max_size; // Maximum size that the icons can be scaled to. 114 size_t max_size; // Maximum size that the icons can be scaled to.
117 size_t min_size; // Minimum size that the icons can be scaled to. 115 size_t min_size; // Minimum size that the icons can be scaled to.
118 size_t threshold; // Maximum difference from desired size. 2 by default. 116 size_t threshold; // Maximum difference from desired size. 2 by default.
119 }; 117 };
120 118
121 explicit IconTheme(const std::string& name); 119 explicit IconTheme(const std::string& name);
122 120
123 ~IconTheme() { 121 ~IconTheme() {}
124 delete[] info_array_;
125 }
126 122
127 // Returns the path to an icon with the name |icon_name| and a size of |size| 123 // Returns the path to an icon with the name |icon_name| and a size of |size|
128 // pixels. If the icon does not exist, but |inherits| is true, then look for 124 // pixels. If the icon does not exist, but |inherits| is true, then look for
129 // the icon in the parent theme. 125 // the icon in the parent theme.
130 FilePath GetIconPath(const std::string& icon_name, int size, bool inherits); 126 FilePath GetIconPath(const std::string& icon_name, int size, bool inherits);
131 127
132 // Load a theme with the name |theme_name| into memory. Returns null if theme 128 // Load a theme with the name |theme_name| into memory. Returns null if theme
133 // is invalid. 129 // is invalid.
134 static IconTheme* LoadTheme(const std::string& theme_name); 130 static IconTheme* LoadTheme(const std::string& theme_name);
135 131
(...skipping 19 matching lines...) Expand all
155 151
156 // Set directories to search for icons to the comma-separated list |dirs|. 152 // Set directories to search for icons to the comma-separated list |dirs|.
157 bool SetDirectories(const std::string& dirs); 153 bool SetDirectories(const std::string& dirs);
158 154
159 bool index_theme_loaded_; // True if an instance is properly loaded. 155 bool index_theme_loaded_; // True if an instance is properly loaded.
160 // store the scattered directories of this theme. 156 // store the scattered directories of this theme.
161 std::list<FilePath> dirs_; 157 std::list<FilePath> dirs_;
162 158
163 // store the subdirs of this theme and array index of |info_array_|. 159 // store the subdirs of this theme and array index of |info_array_|.
164 std::map<std::string, int> subdirs_; 160 std::map<std::string, int> subdirs_;
165 SubDirInfo* info_array_; // List of sub-directories. 161 scoped_array<SubDirInfo> info_array_; // List of sub-directories.
166 std::string inherits_; // Name of the theme this one inherits from. 162 std::string inherits_; // Name of the theme this one inherits from.
167 }; 163 };
168 164
169 IconTheme::IconTheme(const std::string& name) 165 IconTheme::IconTheme(const std::string& name)
170 : index_theme_loaded_(false), 166 : index_theme_loaded_(false),
171 info_array_(NULL) { 167 info_array_(NULL) {
172 base::ThreadRestrictions::AssertIOAllowed(); 168 base::ThreadRestrictions::AssertIOAllowed();
173 // Iterate on all icon directories to find directories of the specified 169 // Iterate on all icon directories to find directories of the specified
174 // theme and load the first encountered index.theme. 170 // theme and load the first encountered index.theme.
175 MimeUtilConstants::IconDirMtimeMap::iterator iter; 171 MimeUtilConstants::IconDirMtimeMap::iterator iter;
176 FilePath theme_path; 172 FilePath theme_path;
177 MimeUtilConstants::IconDirMtimeMap* icon_dirs = 173 MimeUtilConstants::IconDirMtimeMap* icon_dirs =
178 MimeUtilConstants::GetInstance()->icon_dirs_; 174 &MimeUtilConstants::GetInstance()->icon_dirs_;
179 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { 175 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
180 theme_path = iter->first.Append(name); 176 theme_path = iter->first.Append(name);
181 if (!file_util::DirectoryExists(theme_path)) 177 if (!file_util::DirectoryExists(theme_path))
182 continue; 178 continue;
183 FilePath theme_index = theme_path.Append("index.theme"); 179 FilePath theme_index = theme_path.Append("index.theme");
184 if (!index_theme_loaded_ && file_util::PathExists(theme_index)) { 180 if (!index_theme_loaded_ && file_util::PathExists(theme_index)) {
185 if (!LoadIndexTheme(theme_index)) 181 if (!LoadIndexTheme(theme_index))
186 return; 182 return;
187 index_theme_loaded_ = true; 183 index_theme_loaded_ = true;
188 } 184 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 // Inheriting from itself means the theme is buggy but we shouldn't crash. 225 // Inheriting from itself means the theme is buggy but we shouldn't crash.
230 if (theme && theme != this) 226 if (theme && theme != this)
231 return theme->GetIconPath(icon_name, size, inherits); 227 return theme->GetIconPath(icon_name, size, inherits);
232 else 228 else
233 return FilePath(); 229 return FilePath();
234 } 230 }
235 231
236 IconTheme* IconTheme::LoadTheme(const std::string& theme_name) { 232 IconTheme* IconTheme::LoadTheme(const std::string& theme_name) {
237 scoped_ptr<IconTheme> theme; 233 scoped_ptr<IconTheme> theme;
238 MimeUtilConstants::IconThemeMap* icon_themes = 234 MimeUtilConstants::IconThemeMap* icon_themes =
239 MimeUtilConstants::GetInstance()->icon_themes_; 235 &MimeUtilConstants::GetInstance()->icon_themes_;
240 if (icon_themes->find(theme_name) != icon_themes->end()) { 236 if (icon_themes->find(theme_name) != icon_themes->end()) {
241 theme.reset((*icon_themes)[theme_name]); 237 theme.reset((*icon_themes)[theme_name]);
242 } else { 238 } else {
243 theme.reset(new IconTheme(theme_name)); 239 theme.reset(new IconTheme(theme_name));
244 if (!theme->IsValid()) 240 if (!theme->IsValid())
245 theme.reset(); 241 theme.reset();
246 (*icon_themes)[theme_name] = theme.get(); 242 (*icon_themes)[theme_name] = theme.get();
247 } 243 }
248 return theme.release(); 244 return theme.release();
249 } 245 }
(...skipping 25 matching lines...) Expand all
275 while (!feof(fp) && !ferror(fp)) { 271 while (!feof(fp) && !ferror(fp)) {
276 std::string buf = ReadLine(fp); 272 std::string buf = ReadLine(fp);
277 if (buf == "") 273 if (buf == "")
278 break; 274 break;
279 275
280 std::string entry; 276 std::string entry;
281 TrimWhitespaceASCII(buf, TRIM_ALL, &entry); 277 TrimWhitespaceASCII(buf, TRIM_ALL, &entry);
282 if (entry.length() == 0 || entry[0] == '#') { 278 if (entry.length() == 0 || entry[0] == '#') {
283 // Blank line or Comment. 279 // Blank line or Comment.
284 continue; 280 continue;
285 } else if (entry[0] == '[' && info_array_) { 281 } else if (entry[0] == '[' && info_array_.get()) {
286 current_info = NULL; 282 current_info = NULL;
287 std::string subdir = entry.substr(1, entry.length() - 2); 283 std::string subdir = entry.substr(1, entry.length() - 2);
288 if (subdirs_.find(subdir) != subdirs_.end()) 284 if (subdirs_.find(subdir) != subdirs_.end())
289 current_info = &info_array_[subdirs_[subdir]]; 285 current_info = &info_array_[subdirs_[subdir]];
290 } 286 }
291 287
292 std::string key, value; 288 std::string key, value;
293 std::vector<std::string> r; 289 std::vector<std::string> r;
294 base::SplitStringDontTrim(entry, '=', &r); 290 base::SplitStringDontTrim(entry, '=', &r);
295 if (r.size() < 2) 291 if (r.size() < 2)
(...skipping 15 matching lines...) Expand all
311 else if (value == "Threshold") 307 else if (value == "Threshold")
312 current_info->type = SubDirInfo::Threshold; 308 current_info->type = SubDirInfo::Threshold;
313 } else if (key == "MaxSize") { 309 } else if (key == "MaxSize") {
314 current_info->max_size = atoi(value.c_str()); 310 current_info->max_size = atoi(value.c_str());
315 } else if (key == "MinSize") { 311 } else if (key == "MinSize") {
316 current_info->min_size = atoi(value.c_str()); 312 current_info->min_size = atoi(value.c_str());
317 } else if (key == "Threshold") { 313 } else if (key == "Threshold") {
318 current_info->threshold = atoi(value.c_str()); 314 current_info->threshold = atoi(value.c_str());
319 } 315 }
320 } else { 316 } else {
321 if (key.compare("Directories") == 0 && !info_array_) { 317 if (key.compare("Directories") == 0 && !info_array_.get()) {
322 if (!SetDirectories(value)) break; 318 if (!SetDirectories(value)) break;
323 } else if (key.compare("Inherits") == 0) { 319 } else if (key.compare("Inherits") == 0) {
324 if (value != "hicolor") 320 if (value != "hicolor")
325 inherits_ = value; 321 inherits_ = value;
326 } 322 }
327 } 323 }
328 } 324 }
329 325
330 file_util::CloseFile(fp); 326 file_util::CloseFile(fp);
331 return info_array_ != NULL; 327 return info_array_.get() != NULL;
332 } 328 }
333 329
334 size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) { 330 size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) {
335 if (info->type == SubDirInfo::Fixed) { 331 if (info->type == SubDirInfo::Fixed) {
336 if (size > info->size) 332 if (size > info->size)
337 return size - info->size; 333 return size - info->size;
338 else 334 else
339 return info->size - size; 335 return info->size - size;
340 } else if (info->type == SubDirInfo::Scalable) { 336 } else if (info->type == SubDirInfo::Scalable) {
341 if (size < info->min_size) 337 if (size < info->min_size)
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 } 380 }
385 subdirs_[dir] = num++; 381 subdirs_[dir] = num++;
386 pos = epos + 1; 382 pos = epos + 1;
387 } 383 }
388 TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir); 384 TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir);
389 if (dir.length() == 0) { 385 if (dir.length() == 0) {
390 DLOG(WARNING) << "Invalid index.theme: blank subdir"; 386 DLOG(WARNING) << "Invalid index.theme: blank subdir";
391 return false; 387 return false;
392 } 388 }
393 subdirs_[dir] = num++; 389 subdirs_[dir] = num++;
394 info_array_ = new SubDirInfo[num]; 390 info_array_.reset(new SubDirInfo[num]);
395 return true; 391 return true;
396 } 392 }
397 393
398 // Make sure |dir| exists and add it to the list of icon directories. 394 // Make sure |dir| exists and add it to the list of icon directories.
399 void TryAddIconDir(const FilePath& dir) { 395 void TryAddIconDir(const FilePath& dir) {
400 if (!file_util::DirectoryExists(dir)) 396 if (!file_util::DirectoryExists(dir))
401 return; 397 return;
402 (*MimeUtilConstants::GetInstance()->icon_dirs_)[dir] = 0; 398 MimeUtilConstants::GetInstance()->icon_dirs_[dir] = 0;
403 } 399 }
404 400
405 // For a xdg directory |dir|, add the appropriate icon sub-directories. 401 // For a xdg directory |dir|, add the appropriate icon sub-directories.
406 void AddXDGDataDir(const FilePath& dir) { 402 void AddXDGDataDir(const FilePath& dir) {
407 if (!file_util::DirectoryExists(dir)) 403 if (!file_util::DirectoryExists(dir))
408 return; 404 return;
409 TryAddIconDir(dir.Append("icons")); 405 TryAddIconDir(dir.Append("icons"));
410 TryAddIconDir(dir.Append("pixmaps")); 406 TryAddIconDir(dir.Append("pixmaps"));
411 } 407 }
412 408
413 // Add all the xdg icon directories. 409 // Add all the xdg icon directories.
414 void InitIconDir() { 410 void InitIconDir() {
415 MimeUtilConstants::GetInstance()->icon_dirs_->clear(); 411 MimeUtilConstants::GetInstance()->icon_dirs_.clear();
416 FilePath home = file_util::GetHomeDir(); 412 FilePath home = file_util::GetHomeDir();
417 if (!home.empty()) { 413 if (!home.empty()) {
418 FilePath legacy_data_dir(home); 414 FilePath legacy_data_dir(home);
419 legacy_data_dir = legacy_data_dir.AppendASCII(".icons"); 415 legacy_data_dir = legacy_data_dir.AppendASCII(".icons");
420 if (file_util::DirectoryExists(legacy_data_dir)) 416 if (file_util::DirectoryExists(legacy_data_dir))
421 TryAddIconDir(legacy_data_dir); 417 TryAddIconDir(legacy_data_dir);
422 } 418 }
423 const char* env = getenv("XDG_DATA_HOME"); 419 const char* env = getenv("XDG_DATA_HOME");
424 if (env) { 420 if (env) {
425 AddXDGDataDir(FilePath(env)); 421 AddXDGDataDir(FilePath(env));
(...skipping 21 matching lines...) Expand all
447 443
448 // Per xdg theme spec, we should check the icon directories every so often for 444 // Per xdg theme spec, we should check the icon directories every so often for
449 // newly added icons. This isn't quite right. 445 // newly added icons. This isn't quite right.
450 void EnsureUpdated() { 446 void EnsureUpdated() {
451 struct timeval t; 447 struct timeval t;
452 gettimeofday(&t, NULL); 448 gettimeofday(&t, NULL);
453 time_t now = t.tv_sec; 449 time_t now = t.tv_sec;
454 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); 450 MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
455 451
456 if (constants->last_check_time_ == 0) { 452 if (constants->last_check_time_ == 0) {
457 constants->icon_dirs_ = new MimeUtilConstants::IconDirMtimeMap;
458 constants->icon_themes_ = new MimeUtilConstants::IconThemeMap;
459 InitIconDir(); 453 InitIconDir();
460 constants->last_check_time_ = now; 454 constants->last_check_time_ = now;
461 } else { 455 } else {
462 // TODO(thestig): something changed. start over. Upstream fix to Google 456 // TODO(thestig): something changed. start over. Upstream fix to Google
463 // Gadgets for Linux. 457 // Gadgets for Linux.
464 if (now > constants->last_check_time_ + constants->kUpdateInterval) { 458 if (now > constants->last_check_time_ + constants->kUpdateInterval) {
465 } 459 }
466 } 460 }
467 } 461 }
468 462
469 // Find a fallback icon if we cannot find it in the default theme. 463 // Find a fallback icon if we cannot find it in the default theme.
470 FilePath LookupFallbackIcon(const std::string& icon_name) { 464 FilePath LookupFallbackIcon(const std::string& icon_name) {
471 FilePath icon;
472 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); 465 MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
473 MimeUtilConstants::IconDirMtimeMap::iterator iter; 466 MimeUtilConstants::IconDirMtimeMap::iterator iter;
474 MimeUtilConstants::IconDirMtimeMap* icon_dirs = constants->icon_dirs_; 467 MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
475 MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_; 468 MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_;
476 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { 469 for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
477 for (size_t i = 0; i < icon_formats->size(); ++i) { 470 for (size_t i = 0; i < icon_formats->size(); ++i) {
478 icon = iter->first.Append(icon_name + (*icon_formats)[i]); 471 FilePath icon = iter->first.Append(icon_name + (*icon_formats)[i]);
479 if (file_util::PathExists(icon)) 472 if (file_util::PathExists(icon))
480 return icon; 473 return icon;
481 } 474 }
482 } 475 }
483 return FilePath(); 476 return FilePath();
484 } 477 }
485 478
486 // Initialize the list of default themes. 479 // Initialize the list of default themes.
487 void InitDefaultThemes() { 480 void InitDefaultThemes() {
488 IconTheme** default_themes = 481 IconTheme** default_themes =
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
530 if (default_themes[j] == default_themes[i]) 523 if (default_themes[j] == default_themes[i])
531 default_themes[j] = NULL; 524 default_themes[j] = NULL;
532 } 525 }
533 } 526 }
534 } 527 }
535 528
536 // Try to find an icon with the name |icon_name| that's |size| pixels. 529 // Try to find an icon with the name |icon_name| that's |size| pixels.
537 FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) { 530 FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) {
538 EnsureUpdated(); 531 EnsureUpdated();
539 MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); 532 MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
540 MimeUtilConstants::IconThemeMap* icon_themes = constants->icon_themes_; 533 MimeUtilConstants::IconThemeMap* icon_themes = &constants->icon_themes_;
541 if (icon_themes->empty()) 534 if (icon_themes->empty())
542 InitDefaultThemes(); 535 InitDefaultThemes();
543 536
544 FilePath icon_path; 537 FilePath icon_path;
545 IconTheme** default_themes = constants->default_themes_; 538 IconTheme** default_themes = constants->default_themes_;
546 for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) { 539 for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
547 if (default_themes[i]) { 540 if (default_themes[i]) {
548 icon_path = default_themes[i]->GetIconPath(icon_name, size, true); 541 icon_path = default_themes[i]->GetIconPath(icon_name, size, true);
549 if (!icon_path.empty()) 542 if (!icon_path.empty())
550 return icon_path; 543 return icon_path;
551 } 544 }
552 } 545 }
553 return LookupFallbackIcon(icon_name); 546 return LookupFallbackIcon(icon_name);
554 } 547 }
555 548
556 MimeUtilConstants::~MimeUtilConstants() { 549 MimeUtilConstants::~MimeUtilConstants() {
557 delete icon_dirs_;
558 delete icon_themes_;
559 for (size_t i = 0; i < kDefaultThemeNum; i++) 550 for (size_t i = 0; i < kDefaultThemeNum; i++)
560 delete default_themes_[i]; 551 delete default_themes_[i];
561 } 552 }
562 553
563 } // namespace 554 } // namespace
564 555
565 namespace mime_util { 556 namespace mime_util {
566 557
567 std::string GetFileMimeType(const FilePath& filepath) { 558 std::string GetFileMimeType(const FilePath& filepath) {
568 base::ThreadRestrictions::AssertIOAllowed(); 559 base::ThreadRestrictions::AssertIOAllowed();
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
641 } else { 632 } else {
642 icon_file = LookupIconInDefaultTheme(icon_names[i], size); 633 icon_file = LookupIconInDefaultTheme(icon_names[i], size);
643 if (!icon_file.empty()) 634 if (!icon_file.empty())
644 return icon_file; 635 return icon_file;
645 } 636 }
646 } 637 }
647 return FilePath(); 638 return FilePath();
648 } 639 }
649 640
650 } // namespace mime_util 641 } // namespace mime_util
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698