Implement local caching for external extensions on Chrome OS
Default extensions on Chrome OS uses external extensions mechanism on
Chrome OS. Local caching is very important on Chrome OS because it
prevents downloading updates for every user on the machine.
BUG=261215
TEST=manual
[email protected], [email protected]
Review URL: https://codereview.chromium.org/19695010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213690 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/extensions/external_cache.cc b/chrome/browser/chromeos/extensions/external_cache.cc
new file mode 100644
index 0000000..3e19c65
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/external_cache.cc
@@ -0,0 +1,440 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/external_cache.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/location.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/external_provider_impl.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+
+namespace chromeos {
+
+namespace {
+
+// File name extension for CRX files (not case sensitive).
+const char kCRXFileExtension[] = ".crx";
+
+} // namespace
+
+ExternalCache::ExternalCache(const std::string& cache_dir,
+ net::URLRequestContextGetter* request_context,
+ Delegate* delegate)
+ : cache_dir_(cache_dir),
+ request_context_(request_context),
+ delegate_(delegate),
+ cached_extensions_(new base::DictionaryValue()),
+ weak_ptr_factory_(this),
+ worker_pool_token_(
+ content::BrowserThread::GetBlockingPool()->GetSequenceToken()) {
+ notification_registrar_.Add(
+ this,
+ chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
+ content::NotificationService::AllBrowserContextsAndSources());
+}
+
+ExternalCache::~ExternalCache() {
+}
+
+void ExternalCache::UpdateExtensionsList(
+ scoped_ptr<base::DictionaryValue> prefs) {
+ extensions_ = prefs.Pass();
+ CheckCacheNow();
+}
+
+void ExternalCache::OnDamagedFileDetected(const base::FilePath& path) {
+ for (base::DictionaryValue::Iterator it(*cached_extensions_.get());
+ !it.IsAtEnd(); it.Advance()) {
+ const base::DictionaryValue* entry = NULL;
+ if (it.value().GetAsDictionary(&entry)) {
+ NOTREACHED() << "ExternalCache found bad entry with type "
+ << it.value().GetType();
+ continue;
+ }
+
+ std::string external_crx;
+ if (entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
+ &external_crx) &&
+ external_crx == path.value()) {
+
+ LOG(ERROR) << "ExternalCache extension at " << path.value()
+ << " failed to install, deleting it.";
+ cached_extensions_->Remove(it.key(), NULL);
+ UpdateExtensionLoader();
+
+ // The file will be downloaded again on the next restart.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE, FROM_HERE,
+ base::Bind(base::IgnoreResult(base::DeleteFile), path, true));
+
+ // Don't try to DownloadMissingExtensions() from here,
+ // since it can cause a fail/retry loop.
+ return;
+ }
+ }
+ LOG(ERROR) << "ExternalCache cannot find external_crx " << path.value();
+}
+
+void ExternalCache::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
+ extensions::CrxInstaller* installer =
+ content::Source<extensions::CrxInstaller>(source).ptr();
+ OnDamagedFileDetected(installer->source_file());
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ExternalCache::OnExtensionDownloadFailed(
+ const std::string& id,
+ extensions::ExtensionDownloaderDelegate::Error error,
+ const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
+ const std::set<int>& request_ids) {
+ if (error == NO_UPDATE_AVAILABLE) {
+ if (!cached_extensions_->HasKey(id)) {
+ LOG(ERROR) << "ExternalCache extension " << id
+ << " not found on update server";
+ }
+ } else {
+ LOG(ERROR) << "ExternalCache failed to download extension " << id
+ << ", error " << error;
+ }
+}
+
+void ExternalCache::OnExtensionDownloadFinished(
+ const std::string& id,
+ const base::FilePath& path,
+ const GURL& download_url,
+ const std::string& version,
+ const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
+ const std::set<int>& request_ids) {
+ // The explicit copy ctors are to make sure that Bind() binds a copy and not
+ // a reference to the arguments.
+ PostBlockingTask(FROM_HERE,
+ base::Bind(&ExternalCache::BlockingInstallCacheEntry,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::string(cache_dir_),
+ std::string(id),
+ base::FilePath(path),
+ std::string(version)));
+}
+
+void ExternalCache::OnBlacklistDownloadFinished(
+ const std::string& data,
+ const std::string& package_hash,
+ const std::string& version,
+ const extensions::ExtensionDownloaderDelegate::PingResult& ping_result,
+ const std::set<int>& request_ids) {
+ NOTREACHED();
+}
+
+bool ExternalCache::IsExtensionPending(const std::string& id) {
+ // Pending means that there is no installed version yet.
+ return extensions_->HasKey(id) && !cached_extensions_->HasKey(id);
+}
+
+bool ExternalCache::GetExtensionExistingVersion(const std::string& id,
+ std::string* version) {
+ DictionaryValue* extension_dictionary = NULL;
+ if (cached_extensions_->GetDictionary(id, &extension_dictionary)) {
+ return extension_dictionary->GetString(
+ extensions::ExternalProviderImpl::kExternalVersion,
+ version);
+ }
+ return false;
+}
+
+void ExternalCache::CheckCacheNow() {
+ scoped_ptr<DictionaryValue> prefs(extensions_->DeepCopy());
+ PostBlockingTask(FROM_HERE,
+ base::Bind(&ExternalCache::BlockingCheckCache,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::string(cache_dir_),
+ base::Passed(&prefs)));
+}
+
+void ExternalCache::UpdateExtensionLoader() {
+ VLOG(1) << "Notify ExternalCache delegate about cache update";
+ if (delegate_)
+ delegate_->OnExtensionListsUpdated(cached_extensions_.get());
+}
+
+// static
+void ExternalCache::BlockingCheckCache(
+ base::WeakPtr<ExternalCache> external_cache,
+ const std::string& cache_dir,
+ scoped_ptr<base::DictionaryValue> prefs) {
+ BlockingCheckCacheInternal(cache_dir, prefs.get());
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExternalCache::OnCacheUpdated,
+ external_cache,
+ base::Passed(&prefs)));
+}
+
+// static
+void ExternalCache::BlockingCheckCacheInternal(const std::string& cache_dir,
+ base::DictionaryValue* prefs) {
+ // Start by verifying that the cache dir exists.
+ base::FilePath dir(cache_dir);
+ if (!base::DirectoryExists(dir)) {
+ // Create it now.
+ if (!file_util::CreateDirectory(dir)) {
+ LOG(ERROR) << "Failed to create ExternalCache directory at "
+ << dir.value();
+
+ // Nothing else to do. Cache won't be used.
+ return;
+ }
+ }
+
+ // Enumerate all the files in the cache |dir|, including directories
+ // and symlinks. Each unrecognized file will be erased.
+ int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES |
+ base::FileEnumerator::SHOW_SYM_LINKS;
+ base::FileEnumerator enumerator(dir, false /* recursive */, types);
+ for (base::FilePath path = enumerator.Next();
+ !path.empty(); path = enumerator.Next()) {
+ base::FileEnumerator::FileInfo info = enumerator.GetInfo();
+ std::string basename = path.BaseName().value();
+
+ if (info.IsDirectory() || file_util::IsLink(info.GetName())) {
+ LOG(ERROR) << "Erasing bad file in ExternalCache directory: " << basename;
+ base::DeleteFile(path, true /* recursive */);
+ continue;
+ }
+
+ // crx files in the cache are named <extension-id>-<version>.crx.
+ std::string id;
+ std::string version;
+ if (EndsWith(basename, kCRXFileExtension, false /* case-sensitive */)) {
+ size_t n = basename.find('-');
+ if (n != std::string::npos && n + 1 < basename.size() - 4) {
+ id = basename.substr(0, n);
+ // Size of |version| = total size - "<id>" - "-" - ".crx"
+ version = basename.substr(n + 1, basename.size() - 5 - id.size());
+ }
+ }
+
+ base::DictionaryValue* entry = NULL;
+ if (!extensions::Extension::IdIsValid(id)) {
+ LOG(ERROR) << "Bad extension id in ExternalCache: " << id;
+ id.clear();
+ } else if (!prefs->GetDictionary(id, &entry)) {
+ LOG(WARNING) << basename << " is in the cache but is not configured by "
+ << "the ExternalCache source, and will be erased.";
+ id.clear();
+ }
+
+ if (!Version(version).IsValid()) {
+ LOG(ERROR) << "Bad extension version in ExternalCache: " << version;
+ version.clear();
+ }
+
+ if (id.empty() || version.empty()) {
+ LOG(ERROR) << "Invalid file in ExternalCache, erasing: " << basename;
+ base::DeleteFile(path, true /* recursive */);
+ continue;
+ }
+
+ // Enforce a lower-case id.
+ id = StringToLowerASCII(id);
+
+ std::string update_url;
+ std::string prev_version_string;
+ std::string prev_crx;
+ if (entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
+ &update_url)) {
+ VLOG(1) << "ExternalCache found cached version " << version
+ << " for extension id: " << id;
+ entry->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl, NULL);
+ entry->SetString(extensions::ExternalProviderImpl::kExternalVersion,
+ version);
+ entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
+ path.value());
+ if (extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
+ entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore,
+ true);
+ }
+ } else if (
+ entry->GetString(extensions::ExternalProviderImpl::kExternalVersion,
+ &prev_version_string) &&
+ entry->GetString(extensions::ExternalProviderImpl::kExternalCrx,
+ &prev_crx)) {
+ LOG(ERROR) << "Found two ExternalCache files for the same extension, "
+ "will erase the oldest version";
+ Version prev_version(prev_version_string);
+ Version curr_version(version);
+ DCHECK(prev_version.IsValid());
+ DCHECK(curr_version.IsValid());
+ if (prev_version.CompareTo(curr_version) < 0) {
+ base::DeleteFile(base::FilePath(prev_crx), true /* recursive */);
+ entry->SetString(extensions::ExternalProviderImpl::kExternalCrx,
+ path.value());
+ } else {
+ base::DeleteFile(path, true /* recursive */);
+ }
+ } else {
+ NOTREACHED() << "ExternalCache found bad entry for extension id: " << id
+ << " file path: " << path.value();
+ }
+ }
+}
+
+void ExternalCache::OnCacheUpdated(scoped_ptr<base::DictionaryValue> prefs) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // If request_context_ is missing we can't download anything.
+ if (!downloader_ && request_context_) {
+ downloader_.reset(
+ new extensions::ExtensionDownloader(this, request_context_));
+ }
+
+ cached_extensions_->Clear();
+ for (base::DictionaryValue::Iterator it(*extensions_.get());
+ !it.IsAtEnd(); it.Advance()) {
+ const base::DictionaryValue* entry = NULL;
+ if (!it.value().GetAsDictionary(&entry)) {
+ LOG(ERROR) << "ExternalCache found bad entry with type "
+ << it.value().GetType();
+ continue;
+ }
+
+ // Check for updates for all extensions configured except for extensions
+ // marked as keep_if_present.
+ std::string update_url;
+ if (downloader_ &&
+ !entry->HasKey(extensions::ExternalProviderImpl::kKeepIfPresent) &&
+ entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
+ &update_url)) {
+ downloader_->AddPendingExtension(it.key(), GURL(update_url), 0);
+ }
+
+ base::DictionaryValue* cached_entry = NULL;
+ if (prefs->GetDictionary(it.key(), &cached_entry)) {
+ std::string crx_path;
+ if (!downloader_ ||
+ cached_entry->GetString(
+ extensions::ExternalProviderImpl::kExternalCrx, &crx_path) ||
+ cached_entry->HasKey(
+ extensions::ExternalProviderImpl::kKeepIfPresent)) {
+ base::Value* value = NULL;
+ prefs->Remove(it.key(), &value);
+ cached_extensions_->Set(it.key(), value);
+ }
+ }
+ }
+ if (downloader_)
+ downloader_->StartAllPending();
+
+ VLOG(1) << "Updated ExternalCache, there are "
+ << cached_extensions_->size() << " extensions cached";
+
+ UpdateExtensionLoader();
+}
+
+// static
+void ExternalCache::BlockingInstallCacheEntry(
+ base::WeakPtr<ExternalCache> external_cache,
+ const std::string& app_cache_dir,
+ const std::string& id,
+ const base::FilePath& path,
+ const std::string& version) {
+ Version version_validator(version);
+ if (!version_validator.IsValid()) {
+ LOG(ERROR) << "ExternalCache downloaded extension " << id << " but got bad "
+ << "version: " << version;
+ base::DeleteFile(path, true /* recursive */);
+ return;
+ }
+
+ std::string basename = id + "-" + version + kCRXFileExtension;
+ base::FilePath cache_dir(app_cache_dir);
+ base::FilePath cached_crx_path = cache_dir.Append(basename);
+
+ if (base::PathExists(cached_crx_path)) {
+ LOG(WARNING) << "AppPack downloaded a crx whose filename will overwrite "
+ << "an existing cached crx.";
+ base::DeleteFile(cached_crx_path, true /* recursive */);
+ }
+
+ if (!base::DirectoryExists(cache_dir)) {
+ LOG(ERROR) << "AppPack cache directory does not exist, creating now: "
+ << cache_dir.value();
+ if (!file_util::CreateDirectory(cache_dir)) {
+ LOG(ERROR) << "Failed to create the AppPack cache dir!";
+ base::DeleteFile(path, true /* recursive */);
+ return;
+ }
+ }
+
+ if (!base::Move(path, cached_crx_path)) {
+ LOG(ERROR) << "Failed to move AppPack crx from " << path.value()
+ << " to " << cached_crx_path.value();
+ base::DeleteFile(path, true /* recursive */);
+ return;
+ }
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExternalCache::OnCacheEntryInstalled,
+ external_cache,
+ std::string(id),
+ cached_crx_path.value(),
+ std::string(version)));
+}
+
+void ExternalCache::OnCacheEntryInstalled(const std::string& id,
+ const std::string& path,
+ const std::string& version) {
+ VLOG(1) << "AppPack installed a new extension in the cache: " << path;
+
+ base::DictionaryValue* entry = NULL;
+ std::string update_url;
+ if (!extensions_->GetDictionary(id, &entry) ||
+ !entry->GetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
+ &update_url)) {
+ LOG(ERROR) << "ExternalCache cannot find entry for extension " << id;
+ return;
+ }
+
+ // Copy entry to don't modify it inside extensions_.
+ entry = entry->DeepCopy();
+ entry->Remove(extensions::ExternalProviderImpl::kExternalUpdateUrl, NULL);
+ entry->SetString(extensions::ExternalProviderImpl::kExternalVersion, version);
+ entry->SetString(extensions::ExternalProviderImpl::kExternalCrx, path);
+ if (extension_urls::IsWebstoreUpdateUrl(GURL(update_url)))
+ entry->SetBoolean(extensions::ExternalProviderImpl::kIsFromWebstore, true);
+
+ cached_extensions_->Set(id, entry);
+ UpdateExtensionLoader();
+}
+
+void ExternalCache::PostBlockingTask(const tracked_objects::Location& location,
+ const base::Closure& task) {
+ content::BrowserThread::GetBlockingPool()->
+ PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, location, task,
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_cache.h b/chrome/browser/chromeos/extensions/external_cache.h
new file mode 100644
index 0000000..e9415b5e
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/external_cache.h
@@ -0,0 +1,172 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_CACHE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_CACHE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace extensions {
+class ExtensionDownloader;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace tracked_objects {
+class Location;
+}
+
+namespace chromeos {
+
+// The ExternalCache manages cache for external extensions.
+class ExternalCache : public content::NotificationObserver,
+ public extensions::ExtensionDownloaderDelegate {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ // Caller owns |prefs|.
+ virtual void OnExtensionListsUpdated(
+ const base::DictionaryValue* prefs) = 0;
+ };
+
+ // The |request_context| is used for the update checks.
+ ExternalCache(const std::string& cache_dir,
+ net::URLRequestContextGetter* request_context,
+ Delegate* delegate);
+ virtual ~ExternalCache();
+
+ // Update list of extensions in cache and force update check for them.
+ // ExternalCache gets ownership of |prefs|.
+ void UpdateExtensionsList(scoped_ptr<base::DictionaryValue> prefs);
+
+ // If a user of one of the ExternalCache's extensions detects that
+ // the extension is damaged then this method can be used to remove it from
+ // the cache and retry to download it after a restart.
+ void OnDamagedFileDetected(const base::FilePath& path);
+
+ protected:
+ // Implementation of content::NotificationObserver:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Implementation of ExtensionDownloaderDelegate:
+ virtual void OnExtensionDownloadFailed(
+ const std::string& id,
+ Error error,
+ const PingResult& ping_result,
+ const std::set<int>& request_ids) OVERRIDE;
+
+ virtual void OnExtensionDownloadFinished(
+ const std::string& id,
+ const base::FilePath& path,
+ const GURL& download_url,
+ const std::string& version,
+ const PingResult& ping_result,
+ const std::set<int>& request_ids) OVERRIDE;
+
+ virtual void OnBlacklistDownloadFinished(
+ const std::string& data,
+ const std::string& package_hash,
+ const std::string& version,
+ const PingResult& ping_result,
+ const std::set<int>& request_ids) OVERRIDE;
+
+ virtual bool IsExtensionPending(const std::string& id) OVERRIDE;
+
+ virtual bool GetExtensionExistingVersion(const std::string& id,
+ std::string* version) OVERRIDE;
+
+ // Starts a cache update check immediately.
+ void CheckCacheNow();
+
+ // Notifies the that the cache has been updated, providing
+ // extensions loader with an updated list of extensions.
+ void UpdateExtensionLoader();
+
+ // Performs a cache update check on the blocking pool. |external_cache| is
+ // used to reply in the UI thread. |prefs| contains the list extensions
+ // anything else is invalid, and should be removed from the cache.
+ // Ownership of |prefs| is transferred to this function.
+ static void BlockingCheckCache(
+ base::WeakPtr<ExternalCache> external_cache,
+ const std::string& app_cache_dir,
+ scoped_ptr<base::DictionaryValue> prefs);
+
+ // Helper for BlockingCheckCache(), updates |prefs|.
+ static void BlockingCheckCacheInternal(
+ const std::string& app_cache_dir,
+ base::DictionaryValue* prefs);
+
+ // Invoked when the cache has been updated. |prefs| contains all the currently
+ // valid crx files in the cache, ownerships is transfered to this function.
+ void OnCacheUpdated(scoped_ptr<base::DictionaryValue> prefs);
+
+ // Invoked to install the downloaded crx file at |path| in the cache.
+ static void BlockingInstallCacheEntry(
+ base::WeakPtr<ExternalCache> external_cache,
+ const std::string& app_cache_dir,
+ const std::string& id,
+ const base::FilePath& path,
+ const std::string& version);
+
+ // Invoked on the UI thread when a new entry has been installed in the cache.
+ void OnCacheEntryInstalled(const std::string& id,
+ const std::string& path,
+ const std::string& version);
+
+ // Helper to post blocking IO tasks to the blocking pool.
+ void PostBlockingTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ // Path to the dir where apps cache is stored.
+ std::string cache_dir_;
+
+ // Request context used by the |downloader_|.
+ net::URLRequestContextGetter* request_context_;
+
+ // Delegate that would like to get notifications about cache updates.
+ Delegate* delegate_;
+
+ // This is the list of extensions currently configured.
+ scoped_ptr<base::DictionaryValue> extensions_;
+
+ // This contains extensions that are both currently configured
+ // and that have a valid crx in the cache.
+ scoped_ptr<base::DictionaryValue> cached_extensions_;
+
+ // Used to download the extensions and to check for updates.
+ scoped_ptr<extensions::ExtensionDownloader> downloader_;
+
+ base::WeakPtrFactory<ExternalCache> weak_ptr_factory_;
+
+ // Observes failures to install CRX files.
+ content::NotificationRegistrar notification_registrar_;
+
+ // Unique sequence token so that tasks posted by the ExternalCache are
+ // executed sequentially in the blocking pool.
+ base::SequencedWorkerPool::SequenceToken worker_pool_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalCache);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_CACHE_H_
diff --git a/chrome/browser/chromeos/extensions/external_pref_cache_loader.cc b/chrome/browser/chromeos/extensions/external_pref_cache_loader.cc
new file mode 100644
index 0000000..cd02dae
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/external_pref_cache_loader.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/external_pref_cache_loader.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/singleton.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace chromeos {
+
+namespace {
+
+// Directory where the extensions are cached.
+const char kPreinstalledAppsCacheDir[] = "/var/cache/external_cache";
+
+} // namespace
+
+ExternalPrefCacheLoader::ExternalPrefCacheLoader(int base_path_id,
+ Options options)
+ : ExternalPrefLoader(base_path_id, options) {
+}
+
+ExternalPrefCacheLoader::~ExternalPrefCacheLoader() {
+}
+
+void ExternalPrefCacheLoader::OnExtensionListsUpdated(
+ const base::DictionaryValue* prefs) {
+ prefs_.reset(prefs->DeepCopy());
+ ExternalPrefLoader::LoadFinished();
+}
+
+void ExternalPrefCacheLoader::LoadFinished() {
+ if (!external_cache_.get()) {
+ external_cache_.reset(new ExternalCache(kPreinstalledAppsCacheDir,
+ g_browser_process->system_request_context(),
+ this));
+ }
+
+ external_cache_->UpdateExtensionsList(prefs_.Pass());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_pref_cache_loader.h b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h
new file mode 100644
index 0000000..f886f85
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/external_pref_cache_loader.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_PREF_CACHE_LOADER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_PREF_CACHE_LOADER_H_
+
+#include "chrome/browser/chromeos/extensions/external_cache.h"
+#include "chrome/browser/extensions/external_pref_loader.h"
+
+namespace chromeos {
+
+// A specialization of the ExternalPrefCacheLoader that caches crx files for
+// external extensions with update URL in common place for all users on the
+// machine.
+class ExternalPrefCacheLoader : public extensions::ExternalPrefLoader,
+ public ExternalCache::Delegate {
+ public:
+ ExternalPrefCacheLoader(int base_path_id, Options options);
+
+ // Implementation of ExternalCache::Delegate:
+ virtual void OnExtensionListsUpdated(
+ const base::DictionaryValue* prefs) OVERRIDE;
+
+ protected:
+ virtual ~ExternalPrefCacheLoader();
+
+ virtual void LoadFinished() OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<ExternalLoader>;
+
+ scoped_ptr<ExternalCache> external_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalPrefCacheLoader);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_EXTERNAL_PREF_CACHE_LOADER_H_
diff --git a/chrome/browser/chromeos/policy/app_pack_updater.h b/chrome/browser/chromeos/policy/app_pack_updater.h
index 390de7e..9f256b8 100644
--- a/chrome/browser/chromeos/policy/app_pack_updater.h
+++ b/chrome/browser/chromeos/policy/app_pack_updater.h
@@ -22,7 +22,6 @@
class GURL;
namespace extensions {
-class CrxInstaller;
class ExtensionDownloader;
class ExternalLoader;
}
@@ -164,10 +163,6 @@
const std::string& path,
const std::string& version);
- // Handles failure to install CRX files. The file is deleted if it came from
- // the cache.
- void OnCrxInstallFailed(extensions::CrxInstaller* installer);
-
// Helper to post blocking IO tasks to the blocking pool.
void PostBlockingTask(const tracked_objects::Location& from_here,
const base::Closure& task);
diff --git a/chrome/browser/extensions/external_loader.h b/chrome/browser/extensions/external_loader.h
index 86ffee35b..ecadda0 100644
--- a/chrome/browser/extensions/external_loader.h
+++ b/chrome/browser/extensions/external_loader.h
@@ -55,7 +55,7 @@
virtual ~ExternalLoader();
// Notifies the provider that the list of extensions has been loaded.
- void LoadFinished();
+ virtual void LoadFinished();
// Used for passing the list of extensions from the method that loads them
// to |LoadFinished|. To ensure thread safety, the rules are the following:
diff --git a/chrome/browser/extensions/external_pref_loader.h b/chrome/browser/extensions/external_pref_loader.h
index 49b0ce9..c06dd350 100644
--- a/chrome/browser/extensions/external_pref_loader.h
+++ b/chrome/browser/extensions/external_pref_loader.h
@@ -38,6 +38,8 @@
virtual const base::FilePath GetBaseCrxFilePath() OVERRIDE;
protected:
+ virtual ~ExternalPrefLoader() {}
+
virtual void StartLoading() OVERRIDE;
bool IsOptionSet(Options option) {
return (options_ & option) != 0;
@@ -46,8 +48,6 @@
private:
friend class base::RefCountedThreadSafe<ExternalLoader>;
- virtual ~ExternalPrefLoader() {}
-
// Actually searches for and loads candidate standalone extension preference
// files in the path corresponding to |base_path_id|.
// Must be called on the file thread.
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index b5f3dc5..677f4471 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -39,6 +39,7 @@
#endif
#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/extensions/external_pref_cache_loader.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/policy/app_pack_updater.h"
#include "chrome/browser/policy/browser_policy_connector.h"
@@ -394,13 +395,19 @@
external_apps_path_id = chrome::DIR_MANAGED_USERS_DEFAULT_APPS;
#endif
+#if defined(OS_CHROMEOS)
+ typedef chromeos::ExternalPrefCacheLoader PrefLoader;
+#else
+ typedef ExternalPrefLoader PrefLoader;
+#endif
+
if (!is_chromeos_demo_session) {
provider_list->push_back(
linked_ptr<ExternalProviderInterface>(
new ExternalProviderImpl(
service,
- new ExternalPrefLoader(external_apps_path_id,
- check_admin_permissions_on_mac),
+ new PrefLoader(external_apps_path_id,
+ check_admin_permissions_on_mac),
profile,
Manifest::EXTERNAL_PREF,
Manifest::EXTERNAL_PREF_DOWNLOAD,
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 3e713d7..cd0b95a6 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -320,6 +320,10 @@
'browser/chromeos/extensions/default_app_order.h',
'browser/chromeos/extensions/echo_private_api.cc',
'browser/chromeos/extensions/echo_private_api.h',
+ 'browser/chromeos/extensions/external_cache.cc',
+ 'browser/chromeos/extensions/external_cache.h',
+ 'browser/chromeos/extensions/external_pref_cache_loader.cc',
+ 'browser/chromeos/extensions/external_pref_cache_loader.h',
'browser/chromeos/extensions/file_manager/file_handler_util.cc',
'browser/chromeos/extensions/file_manager/file_handler_util.h',
'browser/chromeos/extensions/file_manager/file_manager_event_router.cc',