Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / extensions / external_cache.cc
blobdf59b97f6abc9310a374e22640635af6e2d3aaf0
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"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.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/chrome_extension_downloader_factory.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "extensions/browser/notification_types.h"
24 #include "extensions/browser/updater/extension_downloader.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_urls.h"
27 #include "net/url_request/url_request_context_getter.h"
29 namespace chromeos {
31 ExternalCache::ExternalCache(const base::FilePath& cache_dir,
32 net::URLRequestContextGetter* request_context,
33 const scoped_refptr<base::SequencedTaskRunner>&
34 backend_task_runner,
35 Delegate* delegate,
36 bool always_check_updates,
37 bool wait_for_cache_initialization)
38 : local_cache_(cache_dir, 0, base::TimeDelta(), backend_task_runner),
39 request_context_(request_context),
40 backend_task_runner_(backend_task_runner),
41 delegate_(delegate),
42 always_check_updates_(always_check_updates),
43 wait_for_cache_initialization_(wait_for_cache_initialization),
44 cached_extensions_(new base::DictionaryValue()),
45 weak_ptr_factory_(this) {
46 notification_registrar_.Add(
47 this,
48 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
49 content::NotificationService::AllBrowserContextsAndSources());
52 ExternalCache::~ExternalCache() {
55 void ExternalCache::Shutdown(const base::Closure& callback) {
56 local_cache_.Shutdown(callback);
59 void ExternalCache::UpdateExtensionsList(
60 scoped_ptr<base::DictionaryValue> prefs) {
61 extensions_ = prefs.Pass();
63 if (extensions_->empty()) {
64 // If list of know extensions is empty, don't init cache on disk. It is
65 // important shortcut for test to don't wait forever for cache dir
66 // initialization that should happen outside of Chrome on real device.
67 cached_extensions_->Clear();
68 UpdateExtensionLoader();
69 return;
72 if (local_cache_.is_uninitialized()) {
73 local_cache_.Init(wait_for_cache_initialization_,
74 base::Bind(&ExternalCache::CheckCache,
75 weak_ptr_factory_.GetWeakPtr()));
76 } else {
77 CheckCache();
81 void ExternalCache::OnDamagedFileDetected(const base::FilePath& path) {
82 for (base::DictionaryValue::Iterator it(*cached_extensions_.get());
83 !it.IsAtEnd(); it.Advance()) {
84 const base::DictionaryValue* entry = NULL;
85 if (!it.value().GetAsDictionary(&entry)) {
86 NOTREACHED() << "ExternalCache found bad entry with type "
87 << it.value().GetType();
88 continue;
91 std::string external_crx;
92 if (entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
93 &external_crx) &&
94 external_crx == path.value()) {
95 std::string id = it.key();
96 LOG(ERROR) << "ExternalCache extension at " << path.value()
97 << " failed to install, deleting it.";
98 cached_extensions_->Remove(id, NULL);
99 extensions_->Remove(id, NULL);
101 local_cache_.RemoveExtension(id, std::string());
102 UpdateExtensionLoader();
104 // Don't try to DownloadMissingExtensions() from here,
105 // since it can cause a fail/retry loop.
106 return;
109 DLOG(ERROR) << "ExternalCache cannot find external_crx " << path.value();
112 void ExternalCache::RemoveExtensions(const std::vector<std::string>& ids) {
113 if (ids.empty())
114 return;
116 for (size_t i = 0; i < ids.size(); ++i) {
117 cached_extensions_->Remove(ids[i], NULL);
118 extensions_->Remove(ids[i], NULL);
119 local_cache_.RemoveExtension(ids[i], std::string());
121 UpdateExtensionLoader();
124 bool ExternalCache::GetExtension(const std::string& id,
125 base::FilePath* file_path,
126 std::string* version) {
127 return local_cache_.GetExtension(id, std::string(), file_path, version);
130 void ExternalCache::PutExternalExtension(
131 const std::string& id,
132 const base::FilePath& crx_file_path,
133 const std::string& version,
134 const PutExternalExtensionCallback& callback) {
135 local_cache_.PutExtension(
136 id, std::string(), crx_file_path, version,
137 base::Bind(&ExternalCache::OnPutExternalExtension,
138 weak_ptr_factory_.GetWeakPtr(), id, callback));
141 void ExternalCache::Observe(int type,
142 const content::NotificationSource& source,
143 const content::NotificationDetails& details) {
144 switch (type) {
145 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
146 extensions::CrxInstaller* installer =
147 content::Source<extensions::CrxInstaller>(source).ptr();
148 OnDamagedFileDetected(installer->source_file());
149 break;
152 default:
153 NOTREACHED();
157 void ExternalCache::OnExtensionDownloadFailed(
158 const std::string& id,
159 extensions::ExtensionDownloaderDelegate::Error error,
160 const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
161 const std::set<int>& request_ids) {
162 if (error == NO_UPDATE_AVAILABLE) {
163 if (!cached_extensions_->HasKey(id)) {
164 LOG(ERROR) << "ExternalCache extension " << id
165 << " not found on update server";
166 delegate_->OnExtensionDownloadFailed(id, error);
167 } else {
168 // No version update for an already cached extension.
169 delegate_->OnExtensionLoadedInCache(id);
171 } else {
172 LOG(ERROR) << "ExternalCache failed to download extension " << id
173 << ", error " << error;
174 delegate_->OnExtensionDownloadFailed(id, error);
178 void ExternalCache::OnExtensionDownloadFinished(
179 const extensions::CRXFileInfo& file,
180 bool file_ownership_passed,
181 const GURL& download_url,
182 const std::string& version,
183 const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
184 const std::set<int>& request_ids,
185 const InstallCallback& callback) {
186 DCHECK(file_ownership_passed);
187 local_cache_.PutExtension(
188 file.extension_id, file.expected_hash, file.path, version,
189 base::Bind(&ExternalCache::OnPutExtension, weak_ptr_factory_.GetWeakPtr(),
190 file.extension_id));
191 if (!callback.is_null())
192 callback.Run(true);
195 bool ExternalCache::IsExtensionPending(const std::string& id) {
196 // Pending means that there is no installed version yet.
197 return extensions_->HasKey(id) && !cached_extensions_->HasKey(id);
200 bool ExternalCache::GetExtensionExistingVersion(const std::string& id,
201 std::string* version) {
202 base::DictionaryValue* extension_dictionary = NULL;
203 if (cached_extensions_->GetDictionary(id, &extension_dictionary)) {
204 if (extension_dictionary->GetString(
205 extensions::ExternalProviderImpl::kExternalVersion, version)) {
206 return true;
208 *version = delegate_->GetInstalledExtensionVersion(id);
209 return !version->empty();
211 return false;
214 void ExternalCache::UpdateExtensionLoader() {
215 VLOG(1) << "Notify ExternalCache delegate about cache update";
216 if (delegate_)
217 delegate_->OnExtensionListsUpdated(cached_extensions_.get());
220 void ExternalCache::CheckCache() {
221 if (local_cache_.is_shutdown())
222 return;
224 // If request_context_ is missing we can't download anything.
225 if (!downloader_ && request_context_.get()) {
226 downloader_ = ChromeExtensionDownloaderFactory::CreateForRequestContext(
227 request_context_.get(), this);
230 cached_extensions_->Clear();
231 for (base::DictionaryValue::Iterator it(*extensions_.get());
232 !it.IsAtEnd(); it.Advance()) {
233 const base::DictionaryValue* entry = NULL;
234 if (!it.value().GetAsDictionary(&entry)) {
235 LOG(ERROR) << "ExternalCache found bad entry with type "
236 << it.value().GetType();
237 continue;
240 bool keep_if_present =
241 entry->HasKey(extensions::ExternalProviderImpl::kKeepIfPresent);
242 std::string external_update_url;
243 entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
244 &external_update_url);
245 if (downloader_ && !keep_if_present) {
246 GURL update_url;
247 if (!external_update_url.empty())
248 update_url = GURL(external_update_url);
249 else if (always_check_updates_)
250 update_url = extension_urls::GetWebstoreUpdateUrl();
252 if (update_url.is_valid())
253 downloader_->AddPendingExtension(it.key(), update_url, 0);
256 base::FilePath file_path;
257 std::string version;
258 std::string hash;
259 if (local_cache_.GetExtension(it.key(), hash, &file_path, &version)) {
260 // Copy entry to don't modify it inside extensions_.
261 base::DictionaryValue* entry_copy = entry->DeepCopy();
263 if (extension_urls::IsWebstoreUpdateUrl(GURL(external_update_url))) {
264 entry_copy->SetBoolean(
265 extensions::ExternalProviderImpl::kIsFromWebstore, true);
267 entry_copy->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl,
268 NULL);
269 entry_copy->SetString(extensions::ExternalProviderImpl::kExternalVersion,
270 version);
271 entry_copy->SetString(extensions::ExternalProviderImpl::kExternalCrx,
272 file_path.value());
273 cached_extensions_->Set(it.key(), entry_copy);
274 } else {
275 bool has_external_crx = entry->HasKey(
276 extensions::ExternalProviderImpl::kExternalCrx);
277 bool is_already_installed =
278 !delegate_->GetInstalledExtensionVersion(it.key()).empty();
279 if (keep_if_present || has_external_crx || is_already_installed) {
280 // Copy entry to don't modify it inside extensions_.
281 cached_extensions_->Set(it.key(), entry->DeepCopy());
286 if (downloader_)
287 downloader_->StartAllPending(NULL);
289 VLOG(1) << "Updated ExternalCache, there are "
290 << cached_extensions_->size() << " extensions cached";
292 UpdateExtensionLoader();
295 void ExternalCache::OnPutExtension(const std::string& id,
296 const base::FilePath& file_path,
297 bool file_ownership_passed) {
298 if (local_cache_.is_shutdown() || file_ownership_passed) {
299 backend_task_runner_->PostTask(FROM_HERE,
300 base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, true));
301 return;
304 VLOG(1) << "ExternalCache installed a new extension in the cache " << id;
306 base::DictionaryValue* entry = NULL;
307 if (!extensions_->GetDictionary(id, &entry)) {
308 LOG(ERROR) << "ExternalCache cannot find entry for extension " << id;
309 return;
312 // Copy entry to don't modify it inside extensions_.
313 entry = entry->DeepCopy();
315 std::string version;
316 std::string hash;
317 if (!local_cache_.GetExtension(id, hash, NULL, &version)) {
318 // Copy entry to don't modify it inside extensions_.
319 LOG(ERROR) << "Can't find installed extension in cache " << id;
320 return;
323 std::string update_url;
324 if (entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
325 &update_url) &&
326 extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
327 entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
329 entry->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl, NULL);
330 entry->SetString(extensions::ExternalProviderImpl::kExternalVersion, version);
331 entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
332 file_path.value());
334 cached_extensions_->Set(id, entry);
335 if (delegate_)
336 delegate_->OnExtensionLoadedInCache(id);
337 UpdateExtensionLoader();
340 void ExternalCache::OnPutExternalExtension(
341 const std::string& id,
342 const PutExternalExtensionCallback& callback,
343 const base::FilePath& file_path,
344 bool file_ownership_passed) {
345 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
346 OnPutExtension(id, file_path, file_ownership_passed);
347 callback.Run(id, !file_ownership_passed);
350 std::string ExternalCache::Delegate::GetInstalledExtensionVersion(
351 const std::string& id) {
352 return std::string();
355 } // namespace chromeos