Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(669)

Unified Diff: chrome/browser/extensions/extension_assets_manager_chromeos.cc

Issue 273193006: Install Chrome OS apps to shared location (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: eliminate race condition between install and uninstall Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/extension_assets_manager_chromeos.cc
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.cc b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ff2379000cd528955e1e4c861c992c28c37f8fbc
--- /dev/null
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2014 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/extensions/extension_assets_manager_chromeos.h"
+
+#include <map>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/memory/singleton.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/sequenced_task_runner.h"
+#include "base/sys_info.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/file_util.h"
+#include "extensions/common/manifest.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+namespace {
+
+// A dictionary that maps shared extension IDs to version/paths/users.
+const char kSharedExtensions[] = "SharedExtensions";
+
+// Name of path attribute in shared extensions map.
+const char kSharedExtensionPath[] = "path";
+
+// Name of users attribute (list of user emails) in shared extensions map.
+const char kSharedExtensionUsers[] = "users";
+
+// Shared install dir overrider for tests only.
+static const base::FilePath* g_shared_install_dir_override = NULL;
+
+// This helper class lives on UI thread only. Main purpose of this class is to
+// track shared installation in progress between multiple profiles.
+class ExtensionAssetsManagerHelper {
+ public:
+ // Info about pending install request.
+ struct PendingInstallInfo {
+ base::FilePath unpacked_extension_root;
+ base::FilePath local_install_dir;
+ Profile* profile;
+ ExtensionAssetsManager::InstallExtensionCallback callback;
+ };
+ typedef std::vector<PendingInstallInfo> PendingInstallList;
+
+ static ExtensionAssetsManagerHelper* GetInstance() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ return Singleton<ExtensionAssetsManagerHelper>::get();
+ }
+
+ // Remember that shared install is in progress. Return true if there is no
+ // other installs for given id and version.
+ bool RecordSharedInstall(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ ExtensionAssetsManager::InstallExtensionCallback callback) {
+ PendingInstallInfo install_info;
+ install_info.unpacked_extension_root = unpacked_extension_root;
+ install_info.local_install_dir = local_install_dir;
+ install_info.profile = profile;
+ install_info.callback = callback;
+
+ std::vector<PendingInstallInfo>& callbacks =
+ install_queue_[InstallQueue::key_type(id, version)];
+ callbacks.push_back(install_info);
+
+ return callbacks.size() == 1;
+ }
+
+ // Remove record about shared installation in progress and return
+ // |pending_installs|.
+ void SharedInstallDone(const std::string& id,
+ const std::string& version,
+ PendingInstallList* pending_installs) {
+ InstallQueue::iterator it = install_queue_.find(
+ InstallQueue::key_type(id, version));
+ DCHECK(it != install_queue_.end());
+ pending_installs->swap(it->second);
+ install_queue_.erase(it);
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<ExtensionAssetsManagerHelper>;
+
+ ExtensionAssetsManagerHelper() {}
+ ~ExtensionAssetsManagerHelper() {}
+
+ // Extension ID + version pair.
+ typedef std::pair<std::string, std::string> InstallItem;
+
+ // Queue of pending installs in progress.
+ typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue;
+
+ InstallQueue install_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper);
+};
+
+} // namespace
+
+// Path to shared extensions install dir.
+const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] =
+ "/var/cache/shared_extensions";
+
+ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { }
+
+ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() {
+ if (g_shared_install_dir_override) {
+ delete g_shared_install_dir_override;
+ g_shared_install_dir_override = NULL;
+ }
+}
+
+// static
+ExtensionAssetsManagerChromeOS* ExtensionAssetsManagerChromeOS::GetInstance() {
+ return Singleton<ExtensionAssetsManagerChromeOS>::get();
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::RegisterPrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(kSharedExtensions);
+}
+
+void ExtensionAssetsManagerChromeOS::InstallExtension(
+ const Extension* extension,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ InstallExtensionCallback callback) {
+ if (!CanShareAssets(extension)) {
+ InstallLocalExtension(extension->id(),
+ extension->VersionString(),
+ unpacked_extension_root,
+ local_install_dir,
+ callback);
+ return;
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::CheckSharedExtension,
+ extension->id(),
+ extension->VersionString(),
+ unpacked_extension_root,
+ local_install_dir,
+ profile,
+ callback));
+}
+
+void ExtensionAssetsManagerChromeOS::UninstallExtension(
+ const std::string& id,
+ Profile* profile,
+ const base::FilePath& local_install_dir,
+ const base::FilePath& extension_root) {
+ if (local_install_dir.IsParent(extension_root)) {
+ file_util::UninstallExtension(local_install_dir, id);
+ return;
+ }
+
+ if (GetSharedInstallDir().IsParent(extension_root)) {
+ // In some test extensions installed outside local_install_dir emulate
+ // previous behavior that just do nothing in this case.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused,
+ id,
+ profile));
+ }
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(
+ const base::FilePath& install_dir) {
+ DCHECK(!g_shared_install_dir_override);
+ g_shared_install_dir_override = new base::FilePath(install_dir);
+}
+
+// static
+base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner(
+ Profile* profile) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ ExtensionService* extension_service =
+ ExtensionSystem::Get(profile)->extension_service();
+ return extension_service->GetFileTaskRunner();
+}
+
+// static
+base::FilePath ExtensionAssetsManagerChromeOS::GetSharedInstallDir() {
+ if (g_shared_install_dir_override)
+ return *g_shared_install_dir_override;
+ else
+ return base::FilePath(kSharedExtensionsDir);
+}
+
+// static
+bool ExtensionAssetsManagerChromeOS::CanShareAssets(
+ const Extension* extension) {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableExtensionAssetsSharing)) {
+ return false;
+ }
+
+ // Chrome caches crx files for installed by default apps so sharing assets is
+ // also possible. User specific apps should be excluded to not expose apps
+ // unique for the user outside of user's cryptohome.
+ return Manifest::IsExternalLocation(extension->location());
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::CheckSharedExtension(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ InstallExtensionCallback callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ PrefService* local_state = g_browser_process->local_state();
+ DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+ base::DictionaryValue* extension_info = NULL;
+ base::DictionaryValue* version_info = NULL;
+ base::ListValue* users = NULL;
+ std::string shared_path;
+ if (shared_extensions->GetDictionary(id, &extension_info) &&
+ extension_info->GetDictionaryWithoutPathExpansion(
+ version, &version_info) &&
+ version_info->GetString(kSharedExtensionPath, &shared_path) &&
+ version_info->GetList(kSharedExtensionUsers, &users)) {
+ // This extension version already in shared location.
+ const std::string& user_name = profile->GetProfileName();
+ size_t users_size = users->GetSize();
+ bool user_found = false;
+ for (size_t i = 0; i < users_size; i++) {
+ std::string temp;
+ if (users->GetString(i, &temp) && temp == user_name) {
+ // Re-installation for the same user.
+ user_found = true;
+ break;
+ }
+ }
+ if (!user_found)
+ users->AppendString(user_name);
+
+ // unpacked_extension_root will be deleted by CrxInstaller.
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+ FROM_HERE,
+ base::Bind(callback, base::FilePath(shared_path)));
+ } else {
+ // Desired version is not found in shared location.
+ ExtensionAssetsManagerHelper* helper =
+ ExtensionAssetsManagerHelper::GetInstance();
+ if (helper->RecordSharedInstall(id, version, unpacked_extension_root,
+ local_install_dir, profile, callback)) {
+ // There is no install in progress for given <id, version> so run install.
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+ FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtension,
+ id,
+ version,
+ unpacked_extension_root));
+ }
+ }
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::InstallSharedExtension(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root) {
+ base::FilePath shared_install_dir = GetSharedInstallDir();
+ base::FilePath shared_version_dir = file_util::InstallExtension(
+ unpacked_extension_root, id, version, shared_install_dir);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone,
+ id, version, shared_version_dir));
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& shared_version_dir) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ ExtensionAssetsManagerHelper* helper =
+ ExtensionAssetsManagerHelper::GetInstance();
+ ExtensionAssetsManagerHelper::PendingInstallList pending_installs;
+ helper->SharedInstallDone(id, version, &pending_installs);
+
+ if (shared_version_dir.empty()) {
+ // Installation to shared location failed, try local dir.
+ // TODO(dpolukhin): add UMA stats reporting.
+ for (size_t i = 0; i < pending_installs.size(); i++) {
+ ExtensionAssetsManagerHelper::PendingInstallInfo& info =
+ pending_installs[i];
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
+ FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension,
+ id,
+ version,
+ info.unpacked_extension_root,
+ info.local_install_dir,
+ info.callback));
+ }
+ return;
+ }
+
+ PrefService* local_state = g_browser_process->local_state();
+ DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+ base::DictionaryValue* extension_info = NULL;
+ if (!shared_extensions->GetDictionary(id, &extension_info)) {
+ extension_info = new base::DictionaryValue;
+ shared_extensions->Set(id, extension_info);
+ }
+
+ CHECK(!shared_extensions->HasKey(version));
+ base::DictionaryValue* version_info = new base::DictionaryValue;
+ extension_info->SetWithoutPathExpansion(version, version_info);
+ version_info->SetString(kSharedExtensionPath, shared_version_dir.value());
+
+ base::ListValue* users = new base::ListValue;
+ version_info->Set(kSharedExtensionUsers, users);
+ for (size_t i = 0; i < pending_installs.size(); i++) {
+ ExtensionAssetsManagerHelper::PendingInstallInfo& info =
+ pending_installs[i];
+ users->AppendString(info.profile->GetProfileName());
+
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
+ FROM_HERE,
+ base::Bind(info.callback, shared_version_dir));
+ }
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::InstallLocalExtension(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ InstallExtensionCallback callback) {
+ callback.Run(file_util::InstallExtension(
+ unpacked_extension_root, id, version, local_install_dir));
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused(
+ const std::string& id,
+ Profile* profile) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ PrefService* local_state = g_browser_process->local_state();
+ DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+ base::DictionaryValue* extension_info = NULL;
+ if (!shared_extensions->GetDictionary(id, &extension_info)) {
+ NOTREACHED();
+ return;
+ }
+
+ std::vector<std::string> versions;
+ versions.reserve(extension_info->size());
+ for (base::DictionaryValue::Iterator it(*extension_info);
+ !it.IsAtEnd();
+ it.Advance()) {
+ versions.push_back(it.key());
+ }
+
+ base::StringValue user_name(profile->GetProfileName());
+ for (std::vector<std::string>::const_iterator it = versions.begin();
+ it != versions.end(); it++) {
+ base::DictionaryValue* version_info = NULL;
+ if (!extension_info->GetDictionaryWithoutPathExpansion(*it,
+ &version_info)) {
+ NOTREACHED();
+ continue;
+ }
+ base::ListValue* users = NULL;
+ if (!version_info->GetList(kSharedExtensionUsers, &users)) {
+ NOTREACHED();
+ continue;
+ }
+ if (users->Remove(user_name, NULL) && !users->GetSize()) {
+ std::string shared_path;
+ if (!version_info->GetString(kSharedExtensionPath, &shared_path)) {
+ NOTREACHED();
+ continue;
+ }
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
+ FROM_HERE,
+ base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion,
+ base::FilePath(shared_path)));
+ extension_info->RemoveWithoutPathExpansion(*it, NULL);
+ }
+ }
+ // Don't remove extension dir in shared location. It will be removed by GC
+ // when it is safe to do avoid race condition between uninstall extension
+ // used by single user and installing the same extension for other user.
asargent_no_longer_on_chrome 2014/05/28 21:44:49 A few grammar nits with this sentence. did you m
Dmitry Polukhin 2014/05/28 21:59:19 Done.
+}
+
+// static
+void ExtensionAssetsManagerChromeOS::DeleteSharedVersion(
+ const base::FilePath& shared_version_dir) {
+ CHECK(GetSharedInstallDir().IsParent(shared_version_dir));
+ base::DeleteFile(shared_version_dir, true); // recursive.
+}
+
+} // namespace extensions
« no previous file with comments | « chrome/browser/extensions/extension_assets_manager_chromeos.h ('k') | chrome/browser/extensions/extension_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698