1 // Copyright 2014 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 "chrome/browser/extensions/updater/extension_cache_impl.h" 6 7 #include "base/bind.h" 8 #include "base/memory/singleton.h" 9 #include "base/metrics/histogram.h" 10 #include "base/sequenced_task_runner.h" 11 #include "base/stl_util.h" 12 #include "base/threading/sequenced_worker_pool.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/extensions/crx_installer.h" 15 #include "chrome/browser/extensions/updater/local_extension_cache.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/notification_details.h" 18 #include "content/public/browser/notification_service.h" 19 #include "content/public/browser/notification_source.h" 20 21 namespace extensions { 22 namespace { 23 24 #if defined(OS_CHROMEOS) 25 const char kLocalCacheDir[] = "/var/cache/external_cache"; 26 #else 27 #error Please define kLocalCacheDir suitable for target OS 28 #endif// Directory where the extensions are cached. 29 30 // Maximum size of local cache on disk. 31 size_t kMaxCacheSize = 100 * 1024 * 1024; 32 33 // Maximum age of unused extensions in cache. 34 const int kMaxCacheAgeDays = 30; 35 36 } // namespace 37 38 // static 39 ExtensionCacheImpl* ExtensionCacheImpl::GetInstance() { 40 return Singleton<ExtensionCacheImpl>::get(); 41 } 42 43 ExtensionCacheImpl::ExtensionCacheImpl() 44 : cache_(new LocalExtensionCache(base::FilePath(kLocalCacheDir), 45 kMaxCacheSize, 46 base::TimeDelta::FromDays(kMaxCacheAgeDays), 47 content::BrowserThread::GetBlockingPool()-> 48 GetSequencedTaskRunnerWithShutdownBehavior( 49 content::BrowserThread::GetBlockingPool()->GetSequenceToken(), 50 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))), 51 weak_ptr_factory_(this) { 52 notification_registrar_.Add( 53 this, 54 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR, 55 content::NotificationService::AllBrowserContextsAndSources()); 56 cache_->Init(true, base::Bind(&ExtensionCacheImpl::OnCacheInitialized, 57 weak_ptr_factory_.GetWeakPtr())); 58 } 59 60 ExtensionCacheImpl::~ExtensionCacheImpl() { 61 } 62 63 void ExtensionCacheImpl::Start(const base::Closure& callback) { 64 if (!cache_ || cache_->is_ready()) { 65 DCHECK(init_callbacks_.empty()); 66 callback.Run(); 67 } else { 68 init_callbacks_.push_back(callback); 69 } 70 } 71 72 void ExtensionCacheImpl::Shutdown(const base::Closure& callback) { 73 if (cache_) 74 cache_->Shutdown(callback); 75 else 76 callback.Run(); 77 } 78 79 void ExtensionCacheImpl::AllowCaching(const std::string& id) { 80 allowed_extensions_.insert(id); 81 } 82 83 bool ExtensionCacheImpl::GetExtension(const std::string& id, 84 base::FilePath* file_path, 85 std::string* version) { 86 if (cache_) 87 return cache_->GetExtension(id, file_path, version); 88 else 89 return false; 90 } 91 92 void ExtensionCacheImpl::PutExtension(const std::string& id, 93 const base::FilePath& file_path, 94 const std::string& version, 95 const PutExtensionCallback& callback) { 96 if (cache_ && ContainsKey(allowed_extensions_, id)) 97 cache_->PutExtension(id, file_path, version, callback); 98 else 99 callback.Run(file_path, true); 100 } 101 102 void ExtensionCacheImpl::OnCacheInitialized() { 103 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin(); 104 it != init_callbacks_.end(); ++it) { 105 it->Run(); 106 } 107 init_callbacks_.clear(); 108 109 uint64 cache_size = 0; 110 size_t extensions_count = 0; 111 if (cache_->GetStatistics(&cache_size, &extensions_count)) { 112 UMA_HISTOGRAM_COUNTS_100("Extensions.ExtensionCacheCount", 113 extensions_count); 114 UMA_HISTOGRAM_MEMORY_MB("Extensions.ExtensionCacheSize", 115 cache_size / (1024 * 1024)); 116 } 117 } 118 119 void ExtensionCacheImpl::Observe(int type, 120 const content::NotificationSource& source, 121 const content::NotificationDetails& details) { 122 if (!cache_) 123 return; 124 125 switch (type) { 126 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: { 127 extensions::CrxInstaller* installer = 128 content::Source<extensions::CrxInstaller>(source).ptr(); 129 // TODO(dpolukhin): remove extension from cache only if installation 130 // failed due to file corruption. 131 cache_->RemoveExtension(installer->expected_id()); 132 break; 133 } 134 135 default: 136 NOTREACHED(); 137 } 138 } 139 140 } // namespace extensions 141