Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / extensions / external_cache.cc
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.
4
5 #include "chrome/browser/chromeos/extensions/external_cache.h"
6
7 #include "base/bind.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"
27
28 namespace chromeos {
29
30 ExternalCache::ExternalCache(const base::FilePath& cache_dir,
31                              net::URLRequestContextGetter* request_context,
32                              const scoped_refptr<base::SequencedTaskRunner>&
33                                  backend_task_runner,
34                              Delegate* delegate,
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),
40       delegate_(delegate),
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(
46       this,
47       extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
48       content::NotificationService::AllBrowserContextsAndSources());
49 }
50
51 ExternalCache::~ExternalCache() {
52 }
53
54 void ExternalCache::Shutdown(const base::Closure& callback) {
55   local_cache_.Shutdown(callback);
56 }
57
58 void ExternalCache::UpdateExtensionsList(
59     scoped_ptr<base::DictionaryValue> prefs) {
60   extensions_ = prefs.Pass();
61
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();
68     return;
69   }
70
71   if (local_cache_.is_uninitialized()) {
72     local_cache_.Init(wait_for_cache_initialization_,
73                       base::Bind(&ExternalCache::CheckCache,
74                                  weak_ptr_factory_.GetWeakPtr()));
75   } else {
76     CheckCache();
77   }
78 }
79
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();
87       continue;
88     }
89
90     std::string external_crx;
91     if (entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
92                          &external_crx) &&
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);
99
100       local_cache_.RemoveExtension(id);
101       UpdateExtensionLoader();
102
103       // Don't try to DownloadMissingExtensions() from here,
104       // since it can cause a fail/retry loop.
105       return;
106     }
107   }
108   LOG(ERROR) << "ExternalCache cannot find external_crx " << path.value();
109 }
110
111 void ExternalCache::RemoveExtensions(const std::vector<std::string>& ids) {
112   if (ids.empty())
113     return;
114
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]);
119   }
120   UpdateExtensionLoader();
121 }
122
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);
127 }
128
129 void ExternalCache::Observe(int type,
130                             const content::NotificationSource& source,
131                             const content::NotificationDetails& details) {
132   switch (type) {
133     case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
134       extensions::CrxInstaller* installer =
135           content::Source<extensions::CrxInstaller>(source).ptr();
136       OnDamagedFileDetected(installer->source_file());
137       break;
138     }
139
140     default:
141       NOTREACHED();
142   }
143 }
144
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);
155     } else {
156       // No version update for an already cached extension.
157       delegate_->OnExtensionLoadedInCache(id);
158     }
159   } else {
160     LOG(ERROR) << "ExternalCache failed to download extension " << id
161                << ", error " << error;
162     delegate_->OnExtensionDownloadFailed(id, error);
163   }
164 }
165
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(),
178                                        id));
179 }
180
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);
184 }
185
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)) {
192       return true;
193     }
194     *version = delegate_->GetInstalledExtensionVersion(id);
195     return !version->empty();
196   }
197   return false;
198 }
199
200 void ExternalCache::UpdateExtensionLoader() {
201   VLOG(1) << "Notify ExternalCache delegate about cache update";
202   if (delegate_)
203     delegate_->OnExtensionListsUpdated(cached_extensions_.get());
204 }
205
206 void ExternalCache::CheckCache() {
207   if (local_cache_.is_shutdown())
208     return;
209
210   // If request_context_ is missing we can't download anything.
211   if (!downloader_ && request_context_) {
212     downloader_.reset(
213         new extensions::ExtensionDownloader(this, request_context_));
214   }
215
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();
223       continue;
224     }
225
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) {
232       GURL update_url;
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();
237
238       if (update_url.is_valid())
239         downloader_->AddPendingExtension(it.key(), update_url, 0);
240     }
241
242     base::FilePath file_path;
243     std::string version;
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();
247
248       if (extension_urls::IsWebstoreUpdateUrl(GURL(external_update_url))) {
249         entry_copy->SetBoolean(
250             extensions::ExternalProviderImpl::kIsFromWebstore, true);
251       }
252       entry_copy->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl,
253                          NULL);
254       entry_copy->SetString(extensions::ExternalProviderImpl::kExternalVersion,
255                             version);
256       entry_copy->SetString(extensions::ExternalProviderImpl::kExternalCrx,
257                             file_path.value());
258       cached_extensions_->Set(it.key(), entry_copy);
259     } else {
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());
267       }
268     }
269   }
270
271   if (downloader_)
272     downloader_->StartAllPending(NULL);
273
274   VLOG(1) << "Updated ExternalCache, there are "
275           << cached_extensions_->size() << " extensions cached";
276
277   UpdateExtensionLoader();
278 }
279
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));
286     return;
287   }
288
289   VLOG(1) << "ExternalCache installed a new extension in the cache " << id;
290
291   base::DictionaryValue* entry = NULL;
292   if (!extensions_->GetDictionary(id, &entry)) {
293     LOG(ERROR) << "ExternalCache cannot find entry for extension " << id;
294     return;
295   }
296
297   // Copy entry to don't modify it inside extensions_.
298   entry = entry->DeepCopy();
299
300   std::string version;
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;
304     return;
305   }
306
307   std::string update_url;
308   if (entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
309                        &update_url) &&
310       extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
311     entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
312   }
313   entry->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl, NULL);
314   entry->SetString(extensions::ExternalProviderImpl::kExternalVersion, version);
315   entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
316                    file_path.value());
317
318   cached_extensions_->Set(id, entry);
319   if (delegate_)
320     delegate_->OnExtensionLoadedInCache(id);
321   UpdateExtensionLoader();
322 }
323
324 std::string ExternalCache::Delegate::GetInstalledExtensionVersion(
325     const std::string& id) {
326   return std::string();
327 }
328
329 }  // namespace chromeos