summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordpolukhin@chromium.org <dpolukhin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-29 22:19:07 +0000
committerdpolukhin@chromium.org <dpolukhin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-29 22:19:07 +0000
commit5a145e8fb630fac405eeed52184b7ad2cda29e6f (patch)
tree24f6775d6d91f03da56f00cff10c2d85ed97a8cc
parentf01b18482a22cad2822df036668a0f3c4db6deef (diff)
downloadchromium_src-5a145e8fb630fac405eeed52184b7ad2cda29e6f.zip
chromium_src-5a145e8fb630fac405eeed52184b7ad2cda29e6f.tar.gz
chromium_src-5a145e8fb630fac405eeed52184b7ad2cda29e6f.tar.bz2
Install Chrome OS apps to shared location
This CL implements install and uninstall to shared location. New functionality is disabled by default and can be enabled only by command line switch. The switch will not be enabled until extensions resources verification is not enabled by default in force mode for all apps/extensions. Follow up CL should add support for GC in shared location. BUG=235263 TEST=browser_tests + manual R=asargent@chromium.org TBR=mnissler@chromium.org Review URL: https://codereview.chromium.org/273193006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273620 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/crx_installer.cc21
-rw-r--r--chrome/browser/extensions/crx_installer.h4
-rw-r--r--chrome/browser/extensions/crx_installer_browsertest.cc40
-rw-r--r--chrome/browser/extensions/extension_assets_manager.cc66
-rw-r--r--chrome/browser/extensions/extension_assets_manager.h53
-rw-r--r--chrome/browser/extensions/extension_assets_manager_chromeos.cc421
-rw-r--r--chrome/browser/extensions/extension_assets_manager_chromeos.h105
-rw-r--r--chrome/browser/extensions/extension_service.cc18
-rw-r--r--chrome/browser/extensions/extension_service.h7
-rw-r--r--chrome/browser/prefs/browser_prefs.cc2
-rw-r--r--chrome/chrome_browser_extensions.gypi4
-rw-r--r--chromeos/chromeos_switches.cc3
-rw-r--r--chromeos/chromeos_switches.h1
-rw-r--r--extensions/browser/extension_prefs.cc19
-rw-r--r--extensions/browser/extension_prefs.h3
15 files changed, 743 insertions, 24 deletions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index d03bc1c..b77769f 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/extensions/convert_user_script.h"
#include "chrome/browser/extensions/convert_web_app.h"
#include "chrome/browser/extensions/crx_installer_error.h"
+#include "chrome/browser/extensions/extension_assets_manager.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h"
@@ -695,11 +696,20 @@ void CrxInstaller::CompleteInstall() {
"Extensions.CrxInstallDirPathLength",
install_directory_.value().length(), 0, 500, 100);
- base::FilePath version_dir =
- file_util::InstallExtension(unpacked_extension_root_,
- extension()->id(),
- extension()->VersionString(),
- install_directory_);
+ ExtensionAssetsManager* assets_manager =
+ ExtensionAssetsManager::GetInstance();
+ assets_manager->InstallExtension(
+ extension(),
+ unpacked_extension_root_,
+ install_directory_,
+ profile(),
+ base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
+}
+
+void CrxInstaller::ReloadExtensionAfterInstall(
+ const base::FilePath& version_dir) {
+ DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
+
if (version_dir.empty()) {
ReportFailureFromFileThread(
CrxInstallerError(
@@ -730,7 +740,6 @@ void CrxInstaller::CompleteInstall() {
LOG(ERROR) << error << " " << extension_id << " " << download_url_;
ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error)));
}
-
}
void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index bb0fc1b..a3f7b10 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -240,6 +240,10 @@ class CrxInstaller
// notify the frontend.
void CompleteInstall();
+ // Reloads extension on File thread and reports installation result back
+ // to UI thread.
+ void ReloadExtensionAfterInstall(const base::FilePath& version_dir);
+
// Result reporting.
void ReportFailureFromFileThread(const CrxInstallerError& error);
void ReportFailureFromUIThread(const CrxInstallerError& error);
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index 1f82604..375b57e 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/at_exit.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/extensions/browser_action_test_util.h"
@@ -15,6 +16,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/test/browser_test_utils.h"
@@ -32,6 +34,8 @@
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/users/fake_user_manager.h"
#include "chrome/browser/chromeos/login/users/user_manager.h"
+#include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
+#include "chromeos/chromeos_switches.h"
#endif
class SkBitmap;
@@ -495,4 +499,40 @@ IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, KioskOnlyTest) {
#endif
}
+#if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, InstallToSharedLocation) {
+ base::ShadowingAtExitManager at_exit_manager;
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ chromeos::switches::kEnableExtensionAssetsSharing);
+ base::ScopedTempDir cache_dir;
+ ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
+ ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(
+ cache_dir.path());
+
+ base::FilePath crx_path = test_data_dir_.AppendASCII("crx_installer/v1.crx");
+ const extensions::Extension* extension = InstallExtension(
+ crx_path, 1, extensions::Manifest::EXTERNAL_PREF);
+ base::FilePath extension_path = extension->path();
+ EXPECT_TRUE(cache_dir.path().IsParent(extension_path));
+ EXPECT_TRUE(base::PathExists(extension_path));
+
+ std::string extension_id = extension->id();
+ UninstallExtension(extension_id);
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ browser()->profile())->extension_service();
+ EXPECT_FALSE(service->GetExtensionById(extension_id, false));
+
+ // In the worst case you need to repeat this up to 3 times to make sure that
+ // all pending tasks we sent from UI thread to task runner and back to UI.
+ for (int i = 0; i < 3; i++) {
+ // Wait for background task completion that sends replay to UI thread.
+ content::BrowserThread::GetBlockingPool()->FlushForTesting();
+ // Wait for UI thread task completion.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ EXPECT_FALSE(base::PathExists(extension_path));
+}
+#endif
+
} // namespace extensions
diff --git a/chrome/browser/extensions/extension_assets_manager.cc b/chrome/browser/extensions/extension_assets_manager.cc
new file mode 100644
index 0000000..df594d0
--- /dev/null
+++ b/chrome/browser/extensions/extension_assets_manager.cc
@@ -0,0 +1,66 @@
+// 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.h"
+
+#include "base/memory/singleton.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/file_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
+#endif
+
+namespace extensions {
+namespace {
+
+class ExtensionAssetsManagerImpl : public ExtensionAssetsManager {
+ public:
+ static ExtensionAssetsManagerImpl* GetInstance() {
+ return Singleton<ExtensionAssetsManagerImpl>::get();
+ }
+
+ // Override from ExtensionAssetsManager.
+ virtual void InstallExtension(const Extension* extension,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ InstallExtensionCallback callback) OVERRIDE {
+ callback.Run(file_util::InstallExtension(
+ unpacked_extension_root,
+ extension->id(),
+ extension->VersionString(),
+ local_install_dir));
+ }
+
+ virtual void UninstallExtension(
+ const std::string& id,
+ Profile* profile,
+ const base::FilePath& local_install_dir,
+ const base::FilePath& extension_root) OVERRIDE {
+ file_util::UninstallExtension(local_install_dir, id);
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<ExtensionAssetsManagerImpl>;
+
+ ExtensionAssetsManagerImpl() {}
+ virtual ~ExtensionAssetsManagerImpl() {}
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerImpl);
+};
+
+} // namespace
+
+// static
+ExtensionAssetsManager* ExtensionAssetsManager::GetInstance() {
+#if defined(OS_CHROMEOS)
+ return ExtensionAssetsManagerChromeOS::GetInstance();
+#else
+ // If not Chrome OS, use trivial implementation that doesn't share anything.
+ return ExtensionAssetsManagerImpl::GetInstance();
+#endif // OS_CHROMEOS
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/extension_assets_manager.h b/chrome/browser/extensions/extension_assets_manager.h
new file mode 100644
index 0000000..ced7d3e
--- /dev/null
+++ b/chrome/browser/extensions/extension_assets_manager.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+
+class Profile;
+
+namespace extensions {
+
+class Extension;
+
+// Assets manager for installed extensions. Some extensions can be installed in
+// a shared place for multiple profiles (users). This class manages install and
+// uninstall. At the time being shared location is used for default apps on
+// Chrome OS only. This class must be used only from the extension file task
+// runner thread.
+class ExtensionAssetsManager {
+ public:
+ // Callback that is invoked when the extension assets are installed.
+ // |file_path| is destination directory on success or empty in case of error.
+ typedef base::Callback<void(const base::FilePath& file_path)>
+ InstallExtensionCallback;
+
+ static ExtensionAssetsManager* GetInstance();
+
+ // Copy extension assets to final location. This location could be under
+ // |local_install_dir| or some common location shared for multiple users.
+ virtual void InstallExtension(const Extension* extension,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ InstallExtensionCallback callback) = 0;
+
+ // Remove extension assets if it is not used by anyone else.
+ virtual void UninstallExtension(const std::string& id,
+ Profile* profile,
+ const base::FilePath& local_install_dir,
+ const base::FilePath& extension_root) = 0;
+
+ protected:
+ virtual ~ExtensionAssetsManager() {}
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_H_
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 0000000..4db4993
--- /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 so, and this avoids a race condition between
+ // concurrent uninstall by one user and install by another.
+}
+
+// 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
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.h b/chrome/browser/extensions/extension_assets_manager_chromeos.h
new file mode 100644
index 0000000..7afacdb
--- /dev/null
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_CHROMEOS_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_CHROMEOS_H_
+
+#include "chrome/browser/extensions/extension_assets_manager.h"
+
+template <typename T> struct DefaultSingletonTraits;
+class PrefRegistrySimple;
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace extensions {
+
+// Chrome OS specific implementation of assets manager that shares default apps
+// between all users on the machine.
+class ExtensionAssetsManagerChromeOS : public ExtensionAssetsManager {
+ public:
+ static ExtensionAssetsManagerChromeOS* GetInstance();
+
+ // Path to shared extensions install dir.
+ static const char kSharedExtensionsDir[];
+
+ // Register shared assets related preferences.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // Override from ExtensionAssetsManager.
+ virtual void InstallExtension(const Extension* extension,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ Profile* profile,
+ InstallExtensionCallback callback) OVERRIDE;
+ virtual void UninstallExtension(
+ const std::string& id,
+ Profile* profile,
+ const base::FilePath& local_install_dir,
+ const base::FilePath& extension_root) OVERRIDE;
+
+ static void SetSharedInstallDirForTesting(const base::FilePath& install_dir);
+
+ private:
+ friend struct DefaultSingletonTraits<ExtensionAssetsManagerChromeOS>;
+
+ ExtensionAssetsManagerChromeOS();
+ virtual ~ExtensionAssetsManagerChromeOS();
+
+ // Should be called on UI thread to get associated file task runner for
+ // the profile.
+ static base::SequencedTaskRunner* GetFileTaskRunner(Profile* profile);
+
+ // Return shared install dir.
+ static base::FilePath GetSharedInstallDir();
+
+ // Return |true| if |extension| can be installed in a shared place for all
+ // users on the device.
+ static bool CanShareAssets(const Extension* extension);
+
+ // Called on the UI thread to check if a given version of the |extension|
+ // already exists at the shared location.
+ static void 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);
+
+ // Called on task runner thread to install extension to shared location.
+ static void InstallSharedExtension(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root);
+
+ // Called on UI thread to process shared install result.
+ static void InstallSharedExtensionDone(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& shared_version_dir);
+
+ // Called on task runner thread to install the extension to local dir call
+ // callback with the result.
+ static void InstallLocalExtension(
+ const std::string& id,
+ const std::string& version,
+ const base::FilePath& unpacked_extension_root,
+ const base::FilePath& local_install_dir,
+ InstallExtensionCallback callback);
+
+ // Called on UI thread to mark that shared version is not used.
+ static void MarkSharedExtensionUnused(const std::string& id,
+ Profile* profile);
+
+ // Called on task runner thread to remove shared version.
+ static void DeleteSharedVersion(const base::FilePath& shared_version_dir);
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerChromeOS);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ASSETS_MANAGER_CHROMEOS_H_
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 726cff9..739f0a4 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/data_deleter.h"
+#include "chrome/browser/extensions/extension_assets_manager.h"
#include "chrome/browser/extensions/extension_disabled_ui.h"
#include "chrome/browser/extensions/extension_error_controller.h"
#include "chrome/browser/extensions/extension_install_ui.h"
@@ -749,9 +750,11 @@ bool ExtensionService::UninstallExtension(
if (!Manifest::IsUnpackedLocation(extension->location())) {
if (!GetFileTaskRunner()->PostTask(
FROM_HERE,
- base::Bind(&extensions::file_util::UninstallExtension,
+ base::Bind(&ExtensionService::UninstallExtensionOnFileThread,
+ extension->id(),
+ profile_,
install_directory_,
- extension->id())))
+ extension->path())))
NOTREACHED();
}
@@ -785,6 +788,17 @@ bool ExtensionService::UninstallExtension(
return true;
}
+// static
+void ExtensionService::UninstallExtensionOnFileThread(
+ const std::string& id,
+ Profile* profile,
+ const base::FilePath& install_dir,
+ const base::FilePath& extension_path) {
+ extensions::ExtensionAssetsManager* assets_manager =
+ extensions::ExtensionAssetsManager::GetInstance();
+ assets_manager->UninstallExtension(id, profile, install_dir, extension_path);
+}
+
bool ExtensionService::IsExtensionEnabled(
const std::string& extension_id) const {
if (registry_->enabled_extensions().Contains(extension_id) ||
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 78fdf22..5159162 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -548,6 +548,13 @@ class ExtensionService
// the actual profile objects to be destroyed.
void OnProfileDestructionStarted();
+ // Called on file task runner thread to uninstall extension.
+ static void UninstallExtensionOnFileThread(
+ const std::string& id,
+ Profile* profile,
+ const base::FilePath& install_dir,
+ const base::FilePath& extension_path);
+
// The normal profile associated with this ExtensionService.
Profile* profile_;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1e04e8b..85b061f 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -160,6 +160,7 @@
#include "chrome/browser/chromeos/status/data_promo_notification.h"
#include "chrome/browser/chromeos/system/automatic_reboot_manager.h"
#include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
+#include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
#include "chrome/browser/metrics/chromeos_metrics_provider.h"
#include "chrome/browser/ui/webui/chromeos/charger_replacement_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
@@ -304,6 +305,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
chromeos::WallpaperManager::RegisterPrefs(registry);
chromeos::StartupUtils::RegisterPrefs(registry);
chromeos::echo_offer::RegisterPrefs(registry);
+ extensions::ExtensionAssetsManagerChromeOS::RegisterPrefs(registry);
policy::AutoEnrollmentClient::RegisterPrefs(registry);
policy::BrowserPolicyConnectorChromeOS::RegisterPrefs(registry);
policy::DeviceCloudPolicyManagerChromeOS::RegisterPrefs(registry);
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 588b601..af4a03a 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -694,6 +694,10 @@
'browser/extensions/extension_action_icon_factory.h',
'browser/extensions/extension_action_manager.cc',
'browser/extensions/extension_action_manager.h',
+ 'browser/extensions/extension_assets_manager.cc',
+ 'browser/extensions/extension_assets_manager.h',
+ 'browser/extensions/extension_assets_manager_chromeos.cc',
+ 'browser/extensions/extension_assets_manager_chromeos.h',
'browser/extensions/extension_commands_global_registry.cc',
'browser/extensions/extension_commands_global_registry.h',
'browser/extensions/extension_context_menu_model.cc',
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 2a52677..c20b740 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -76,6 +76,9 @@ const char kEnableConsumerManagement[] = "enable-consumer-management";
// of GAIA.
const char kEnableEmbeddedSignin[] = "enable-embedded-signin";
+// Enabled sharing assets for installed default apps.
+const char kEnableExtensionAssetsSharing[] = "enable-extension-assets-sharing";
+
// Enables MTP support in Files.app.
const char kEnableFileManagerMTP[] = "enable-filemanager-mtp";
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index a1d2f45..d0ee27c 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -40,6 +40,7 @@ CHROMEOS_EXPORT extern const char kEnableCarrierSwitching[];
CHROMEOS_EXPORT extern const char kEnableChromeVoxNext[];
CHROMEOS_EXPORT extern const char kEnableConsumerManagement[];
CHROMEOS_EXPORT extern const char kEnableEmbeddedSignin[];
+CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
CHROMEOS_EXPORT extern const char kEnableFileManagerMTP[];
CHROMEOS_EXPORT extern const char kEnableRollbackOption[];
CHROMEOS_EXPORT extern const char kEnableHIDDetectionOnOOBE[];
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 2ebbadd..665d364 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -1339,19 +1339,6 @@ void ExtensionPrefs::UpdateManifest(const Extension* extension) {
}
}
-base::FilePath ExtensionPrefs::GetExtensionPath(
- const std::string& extension_id) {
- const base::DictionaryValue* dict = GetExtensionPref(extension_id);
- if (!dict)
- return base::FilePath();
-
- std::string path;
- if (!dict->GetString(kPrefPath, &path))
- return base::FilePath();
-
- return install_directory_.Append(base::FilePath::FromUTF8Unsafe(path));
-}
-
scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledInfoHelper(
const std::string& extension_id,
const base::DictionaryValue* extension) const {
@@ -1366,9 +1353,15 @@ scoped_ptr<ExtensionInfo> ExtensionPrefs::GetInstalledInfoHelper(
// Make path absolute. Unpacked extensions will already have absolute paths,
// otherwise make it so.
Manifest::Location location = static_cast<Manifest::Location>(location_value);
+#if !defined(OS_CHROMEOS)
if (!Manifest::IsUnpackedLocation(location)) {
DCHECK(location == Manifest::COMPONENT ||
!base::FilePath(path).IsAbsolute());
+#else
+ // On Chrome OS some extensions can be installed to shared location and
+ // thus use absolute paths in prefs.
+ if (!base::FilePath(path).IsAbsolute()) {
+#endif // !defined(OS_CHROMEOS)
path = install_directory_.Append(path).value();
}
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index fc65d73..fc3c7be 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -283,9 +283,6 @@ class ExtensionPrefs : public ExtensionScopedPrefs, public KeyedService {
// Called to change the extension's manifest when it's re-localized.
void UpdateManifest(const Extension* extension);
- // Returns extension path based on extension ID, or empty FilePath on error.
- base::FilePath GetExtensionPath(const std::string& extension_id);
-
// Returns base extensions install directory.
const base::FilePath& install_directory() const { return install_directory_; }