Chromium Code Reviews| 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 |