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..126d7402c0b1d7b01ff854110b84affa00b1848f |
--- /dev/null |
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.cc |
@@ -0,0 +1,333 @@ |
+// 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 <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 { |
+ |
+// 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"; |
+ |
+} // namespace |
+ |
+namespace extensions { |
+ |
+// Path to shared extensions install dir. |
+const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] = |
+ "/var/cache/shared_extensions"; |
+ |
+ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() |
+ : weak_ptr_factory_(this), |
+ shared_install_dir_(kSharedExtensionsDir) { |
+} |
+ |
+ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { |
+} |
+ |
+// 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)) { |
+ ExtensionAssetsManager::InstallExtension( |
+ extension, |
+ unpacked_extension_root, |
+ local_install_dir, |
+ profile, |
+ callback); |
+ return; |
+ } |
+ |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ExtensionAssetsManagerChromeOS::CheckSharedExtension, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ extension, |
+ 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)) { |
+ ExtensionAssetsManager::UninstallExtension( |
+ id, profile, local_install_dir, extension_root); |
+ return; |
+ } |
+ |
+ DCHECK(shared_install_dir_.IsParent(extension_root)); |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ id, |
+ profile)); |
+} |
+ |
+void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( |
+ const base::FilePath& install_dir) { |
+ shared_install_dir_ = 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 |
+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 don't expose apps |
asargent_no_longer_on_chrome
2014/05/16 17:56:19
grammar nit: "should be excluded to don't" -> "sho
Dmitry Polukhin
2014/05/16 22:45:50
Done.
|
+ // unique for the user outside of user's cryptohome. |
+ return Manifest::IsExternalLocation(extension->location()); |
+} |
+ |
+// static |
+void ExtensionAssetsManagerChromeOS::CheckSharedExtension( |
+ base::WeakPtr<ExtensionAssetsManagerChromeOS> object, |
+ const Extension* extension, |
+ 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(extension->id(), &extension_info) && |
+ extension_info->GetDictionaryWithoutPathExpansion( |
+ extension->VersionString(), &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(&ExtensionAssetsManagerChromeOS::SharedExtensionFound, |
+ base::FilePath(shared_path), |
+ callback)); |
+ } else { |
+ // Desired version not found in shared location. |
+ ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ExtensionAssetsManagerChromeOS::SharedExtensionNotFound, |
+ object, |
+ extension, |
+ unpacked_extension_root, |
+ local_install_dir, |
+ profile, |
+ callback)); |
+ } |
+} |
+ |
+// static |
+void ExtensionAssetsManagerChromeOS::SharedExtensionFound( |
+ const base::FilePath& shared_version_dir, |
+ InstallExtensionCallback callback) { |
+ callback.Run(shared_version_dir); |
+} |
+ |
+// static |
+void ExtensionAssetsManagerChromeOS::SharedExtensionNotFound( |
+ const Extension* extension, |
+ const base::FilePath& unpacked_extension_root, |
+ const base::FilePath& local_install_dir, |
+ Profile* profile, |
+ InstallExtensionCallback callback) { |
+ base::FilePath shared_install_dir(shared_install_dir_); |
+ base::FilePath shared_version_dir = file_util::InstallExtension( |
+ unpacked_extension_root, |
+ extension->id(), |
+ extension->VersionString(), |
+ shared_install_dir); |
asargent_no_longer_on_chrome
2014/05/16 17:56:19
Will concurrent user sessions (eg a chromebox allo
Dmitry Polukhin
2014/05/16 22:45:50
Yes, in M37 we enabled multi-profile by default on
asargent_no_longer_on_chrome
2014/05/19 18:25:20
I spent a little time thinking about this and happ
|
+ if (shared_version_dir.empty()) { |
+ // Installation to shared location failed, try local dir. |
+ // TODO(dpolukhin): add UMA stats reporting. |
+ LOG(ERROR) << "Cannot install extension to shared location " |
+ << shared_install_dir.value(); |
+ callback.Run(file_util::InstallExtension( |
+ unpacked_extension_root, |
+ extension->id(), |
+ extension->VersionString(), |
+ local_install_dir)); |
+ } else { |
+ // Mark shared dir as used. |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUsed, |
+ extension->id(), |
+ extension->VersionString(), |
+ shared_version_dir, |
+ profile)); |
+ |
+ callback.Run(shared_version_dir); |
+ } |
+} |
+ |
+// static |
+void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUsed( |
+ const std::string& id, |
+ const std::string& version, |
+ const base::FilePath& shared_version_dir, |
+ 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)) { |
+ extension_info = new base::DictionaryValue; |
+ shared_extensions->Set(id, extension_info); |
+ } |
+ |
+ base::DictionaryValue* version_info = NULL; |
+ if (!extension_info->GetDictionaryWithoutPathExpansion(version, |
+ &version_info)) { |
+ version_info = new base::DictionaryValue; |
+ extension_info->SetWithoutPathExpansion(version, version_info); |
+ } |
+ |
+ CHECK(!version_info->HasKey(kSharedExtensionPath)); |
+ version_info->SetString(kSharedExtensionPath, shared_version_dir.value()); |
+ |
+ CHECK(!version_info->HasKey(kSharedExtensionUsers)); |
+ base::ListValue* users = new base::ListValue; |
+ users->AppendString(profile->GetProfileName()); |
+ version_info->Set(kSharedExtensionUsers, users); |
+} |
+ |
+// static |
+void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused( |
+ base::WeakPtr<ExtensionAssetsManagerChromeOS> object, |
+ 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, |
+ object, |
+ base::FilePath(shared_path))); |
+ extension_info->RemoveWithoutPathExpansion(*it, NULL); |
+ } |
+ } |
+ if (!extension_info->size()) |
+ shared_extensions->RemoveWithoutPathExpansion(id, NULL); |
+} |
+ |
+void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( |
+ const base::FilePath& shared_version_dir) { |
+ CHECK(shared_install_dir_.IsParent(shared_version_dir)); |
+ base::DeleteFile(shared_version_dir, true); // recursive. |
+} |
+ |
+} // namespace extensions |