1 // Copyright (c) 2013 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.
5 #include "chrome/browser/chromeos/extensions/external_cache.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/file_util.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/strings/string_util.h"
15 #include "base/values.h"
16 #include "base/version.h"
17 #include "chrome/browser/extensions/crx_installer.h"
18 #include "chrome/browser/extensions/external_provider_impl.h"
19 #include "chrome/browser/extensions/updater/extension_downloader.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_source.h"
24 #include "extensions/browser/notification_types.h"
25 #include "extensions/common/extension.h"
26 #include "net/url_request/url_request_context_getter.h"
30 ExternalCache::ExternalCache(const base::FilePath& cache_dir,
31 net::URLRequestContextGetter* request_context,
32 const scoped_refptr<base::SequencedTaskRunner>&
35 bool always_check_updates,
36 bool wait_for_cache_initialization)
37 : local_cache_(cache_dir, 0, base::TimeDelta(), backend_task_runner),
38 request_context_(request_context),
39 backend_task_runner_(backend_task_runner),
41 always_check_updates_(always_check_updates),
42 wait_for_cache_initialization_(wait_for_cache_initialization),
43 cached_extensions_(new base::DictionaryValue()),
44 weak_ptr_factory_(this) {
45 notification_registrar_.Add(
47 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
48 content::NotificationService::AllBrowserContextsAndSources());
51 ExternalCache::~ExternalCache() {
54 void ExternalCache::Shutdown(const base::Closure& callback) {
55 local_cache_.Shutdown(callback);
58 void ExternalCache::UpdateExtensionsList(
59 scoped_ptr<base::DictionaryValue> prefs) {
60 extensions_ = prefs.Pass();
62 if (extensions_->empty()) {
63 // If list of know extensions is empty, don't init cache on disk. It is
64 // important shortcut for test to don't wait forever for cache dir
65 // initialization that should happen outside of Chrome on real device.
66 cached_extensions_->Clear();
67 UpdateExtensionLoader();
71 if (local_cache_.is_uninitialized()) {
72 local_cache_.Init(wait_for_cache_initialization_,
73 base::Bind(&ExternalCache::CheckCache,
74 weak_ptr_factory_.GetWeakPtr()));
80 void ExternalCache::OnDamagedFileDetected(const base::FilePath& path) {
81 for (base::DictionaryValue::Iterator it(*cached_extensions_.get());
82 !it.IsAtEnd(); it.Advance()) {
83 const base::DictionaryValue* entry = NULL;
84 if (!it.value().GetAsDictionary(&entry)) {
85 NOTREACHED() << "ExternalCache found bad entry with type "
86 << it.value().GetType();
90 std::string external_crx;
91 if (entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
93 external_crx == path.value()) {
94 std::string id = it.key();
95 LOG(ERROR) << "ExternalCache extension at " << path.value()
96 << " failed to install, deleting it.";
97 cached_extensions_->Remove(id, NULL);
98 extensions_->Remove(id, NULL);
100 local_cache_.RemoveExtension(id);
101 UpdateExtensionLoader();
103 // Don't try to DownloadMissingExtensions() from here,
104 // since it can cause a fail/retry loop.
108 LOG(ERROR) << "ExternalCache cannot find external_crx " << path.value();
111 void ExternalCache::RemoveExtensions(const std::vector<std::string>& ids) {
115 for (size_t i = 0; i < ids.size(); ++i) {
116 cached_extensions_->Remove(ids[i], NULL);
117 extensions_->Remove(ids[i], NULL);
118 local_cache_.RemoveExtension(ids[i]);
120 UpdateExtensionLoader();
123 bool ExternalCache::GetExtension(const std::string& id,
124 base::FilePath* file_path,
125 std::string* version) {
126 return local_cache_.GetExtension(id, file_path, version);
129 void ExternalCache::Observe(int type,
130 const content::NotificationSource& source,
131 const content::NotificationDetails& details) {
133 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
134 extensions::CrxInstaller* installer =
135 content::Source<extensions::CrxInstaller>(source).ptr();
136 OnDamagedFileDetected(installer->source_file());
145 void ExternalCache::OnExtensionDownloadFailed(
146 const std::string& id,
147 extensions::ExtensionDownloaderDelegate::Error error,
148 const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
149 const std::set<int>& request_ids) {
150 if (error == NO_UPDATE_AVAILABLE) {
151 if (!cached_extensions_->HasKey(id)) {
152 LOG(ERROR) << "ExternalCache extension " << id
153 << " not found on update server";
154 delegate_->OnExtensionDownloadFailed(id, error);
156 // No version update for an already cached extension.
157 delegate_->OnExtensionLoadedInCache(id);
160 LOG(ERROR) << "ExternalCache failed to download extension " << id
161 << ", error " << error;
162 delegate_->OnExtensionDownloadFailed(id, error);
166 void ExternalCache::OnExtensionDownloadFinished(
167 const std::string& id,
168 const base::FilePath& path,
169 bool file_ownership_passed,
170 const GURL& download_url,
171 const std::string& version,
172 const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
173 const std::set<int>& request_ids) {
174 DCHECK(file_ownership_passed);
175 local_cache_.PutExtension(id, path, version,
176 base::Bind(&ExternalCache::OnPutExtension,
177 weak_ptr_factory_.GetWeakPtr(),
181 bool ExternalCache::IsExtensionPending(const std::string& id) {
182 // Pending means that there is no installed version yet.
183 return extensions_->HasKey(id) && !cached_extensions_->HasKey(id);
186 bool ExternalCache::GetExtensionExistingVersion(const std::string& id,
187 std::string* version) {
188 base::DictionaryValue* extension_dictionary = NULL;
189 if (cached_extensions_->GetDictionary(id, &extension_dictionary)) {
190 if (extension_dictionary->GetString(
191 extensions::ExternalProviderImpl::kExternalVersion, version)) {
194 *version = delegate_->GetInstalledExtensionVersion(id);
195 return !version->empty();
200 void ExternalCache::UpdateExtensionLoader() {
201 VLOG(1) << "Notify ExternalCache delegate about cache update";
203 delegate_->OnExtensionListsUpdated(cached_extensions_.get());
206 void ExternalCache::CheckCache() {
207 if (local_cache_.is_shutdown())
210 // If request_context_ is missing we can't download anything.
211 if (!downloader_ && request_context_) {
213 new extensions::ExtensionDownloader(this, request_context_));
216 cached_extensions_->Clear();
217 for (base::DictionaryValue::Iterator it(*extensions_.get());
218 !it.IsAtEnd(); it.Advance()) {
219 const base::DictionaryValue* entry = NULL;
220 if (!it.value().GetAsDictionary(&entry)) {
221 LOG(ERROR) << "ExternalCache found bad entry with type "
222 << it.value().GetType();
226 bool keep_if_present =
227 entry->HasKey(extensions::ExternalProviderImpl::kKeepIfPresent);
228 std::string external_update_url;
229 entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
230 &external_update_url);
231 if (downloader_ && !keep_if_present) {
233 if (!external_update_url.empty())
234 update_url = GURL(external_update_url);
235 else if (always_check_updates_)
236 update_url = extension_urls::GetWebstoreUpdateUrl();
238 if (update_url.is_valid())
239 downloader_->AddPendingExtension(it.key(), update_url, 0);
242 base::FilePath file_path;
244 if (local_cache_.GetExtension(it.key(), &file_path, &version)) {
245 // Copy entry to don't modify it inside extensions_.
246 base::DictionaryValue* entry_copy = entry->DeepCopy();
248 if (extension_urls::IsWebstoreUpdateUrl(GURL(external_update_url))) {
249 entry_copy->SetBoolean(
250 extensions::ExternalProviderImpl::kIsFromWebstore, true);
252 entry_copy->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl,
254 entry_copy->SetString(extensions::ExternalProviderImpl::kExternalVersion,
256 entry_copy->SetString(extensions::ExternalProviderImpl::kExternalCrx,
258 cached_extensions_->Set(it.key(), entry_copy);
260 bool has_external_crx = entry->HasKey(
261 extensions::ExternalProviderImpl::kExternalCrx);
262 bool is_already_installed =
263 !delegate_->GetInstalledExtensionVersion(it.key()).empty();
264 if (keep_if_present || has_external_crx || is_already_installed) {
265 // Copy entry to don't modify it inside extensions_.
266 cached_extensions_->Set(it.key(), entry->DeepCopy());
272 downloader_->StartAllPending(NULL);
274 VLOG(1) << "Updated ExternalCache, there are "
275 << cached_extensions_->size() << " extensions cached";
277 UpdateExtensionLoader();
280 void ExternalCache::OnPutExtension(const std::string& id,
281 const base::FilePath& file_path,
282 bool file_ownership_passed) {
283 if (local_cache_.is_shutdown() || file_ownership_passed) {
284 backend_task_runner_->PostTask(FROM_HERE,
285 base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, true));
289 VLOG(1) << "ExternalCache installed a new extension in the cache " << id;
291 base::DictionaryValue* entry = NULL;
292 if (!extensions_->GetDictionary(id, &entry)) {
293 LOG(ERROR) << "ExternalCache cannot find entry for extension " << id;
297 // Copy entry to don't modify it inside extensions_.
298 entry = entry->DeepCopy();
301 if (!local_cache_.GetExtension(id, NULL, &version)) {
302 // Copy entry to don't modify it inside extensions_.
303 LOG(ERROR) << "Can't find installed extension in cache " << id;
307 std::string update_url;
308 if (entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
310 extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
311 entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
313 entry->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl, NULL);
314 entry->SetString(extensions::ExternalProviderImpl::kExternalVersion, version);
315 entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
318 cached_extensions_->Set(id, entry);
320 delegate_->OnExtensionLoadedInCache(id);
321 UpdateExtensionLoader();
324 std::string ExternalCache::Delegate::GetInstalledExtensionVersion(
325 const std::string& id) {
326 return std::string();
329 } // namespace chromeos