|  | // Copyright 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_garbage_collector_chromeos.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/strings/string_split.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/values.h" | 
|  | #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" | 
|  | #include "chrome/browser/ash/profiles/profile_helper.h" | 
|  | #include "chrome/browser/extensions/extension_assets_manager_chromeos.h" | 
|  | #include "chrome/browser/extensions/extension_service.h" | 
|  | #include "chrome/browser/extensions/extension_service_test_base.h" | 
|  | #include "chrome/browser/prefs/browser_prefs.h" | 
|  | #include "chrome/browser/profiles/profile.h" | 
|  | #include "chrome/test/base/testing_browser_process.h" | 
|  | #include "chrome/test/base/testing_profile.h" | 
|  | #include "components/account_id/account_id.h" | 
|  | #include "components/prefs/scoped_user_pref_update.h" | 
|  | #include "components/prefs/testing_pref_service.h" | 
|  | #include "components/user_manager/scoped_user_manager.h" | 
|  | #include "components/user_manager/user_manager.h" | 
|  | #include "components/user_manager/user_names.h" | 
|  | #include "content/public/browser/plugin_service.h" | 
|  | #include "content/public/test/test_utils.h" | 
|  | #include "extensions/browser/extension_prefs.h" | 
|  | #include "extensions/browser/install_flag.h" | 
|  | #include "extensions/common/extension_builder.h" | 
|  | #include "extensions/common/value_builder.h" | 
|  | #include "ppapi/buildflags/buildflags.h" | 
|  |  | 
|  | namespace { | 
|  | const char kExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | 
|  | const char kExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; | 
|  | }  // namespace | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | class ExtensionGarbageCollectorChromeOSUnitTest | 
|  | : public ExtensionServiceTestBase { | 
|  | protected: | 
|  | const base::FilePath& cache_dir() { return cache_dir_.GetPath(); } | 
|  |  | 
|  | void SetUp() override { | 
|  | ExtensionServiceTestBase::SetUp(); | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PLUGINS) | 
|  | content::PluginService::GetInstance()->Init(); | 
|  | #endif | 
|  | InitializeGoodInstalledExtensionService(); | 
|  |  | 
|  | CHECK(cache_dir_.CreateUniqueTempDir()); | 
|  | ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(cache_dir()); | 
|  | ExtensionGarbageCollectorChromeOS::ClearGarbageCollectedForTesting(); | 
|  |  | 
|  | // Initialize the UserManager singleton to a fresh FakeChromeUserManager | 
|  | // instance. | 
|  | user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>( | 
|  | std::make_unique<ash::FakeChromeUserManager>()); | 
|  |  | 
|  | GetFakeUserManager()->AddUser(user_manager::StubAccountId()); | 
|  | GetFakeUserManager()->LoginUser(user_manager::StubAccountId()); | 
|  | chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting( | 
|  | GetFakeUserManager()->GetActiveUser(), profile_.get()); | 
|  | } | 
|  |  | 
|  | void GarbageCollectExtensions() { | 
|  | ExtensionGarbageCollector::Get(profile_.get()) | 
|  | ->GarbageCollectExtensionsForTest(); | 
|  | // Wait for GarbageCollectExtensions task to complete. | 
|  | content::RunAllTasksUntilIdle(); | 
|  | } | 
|  |  | 
|  | base::FilePath CreateSharedExtensionDir(const std::string& id, | 
|  | const std::string& version, | 
|  | const base::FilePath& shared_dir) { | 
|  | base::FilePath path = shared_dir.AppendASCII(id).AppendASCII(version); | 
|  | CreateDirectory(path); | 
|  | return path; | 
|  | } | 
|  |  | 
|  | void CreateSharedExtensionPrefs(const std::string& id, | 
|  | const std::string& version, | 
|  | const std::string& users_string, | 
|  | const base::FilePath& path) { | 
|  | DictionaryPrefUpdate shared_extensions(testing_local_state_.Get(), | 
|  | ExtensionAssetsManagerChromeOS::kSharedExtensions); | 
|  |  | 
|  | base::DictionaryValue* extension_info_weak = NULL; | 
|  | if (!shared_extensions->GetDictionary(id, &extension_info_weak)) { | 
|  | auto extension_info = std::make_unique<base::DictionaryValue>(); | 
|  | extension_info_weak = extension_info.get(); | 
|  | shared_extensions->Set(id, std::move(extension_info)); | 
|  | } | 
|  |  | 
|  | auto version_info = std::make_unique<base::DictionaryValue>(); | 
|  | version_info->SetString( | 
|  | ExtensionAssetsManagerChromeOS::kSharedExtensionPath, path.value()); | 
|  |  | 
|  | auto users = std::make_unique<base::ListValue>(); | 
|  | for (const std::string& user : | 
|  | base::SplitString(users_string, ",", base::KEEP_WHITESPACE, | 
|  | base::SPLIT_WANT_NONEMPTY)) { | 
|  | users->AppendString(user); | 
|  | } | 
|  | version_info->Set(ExtensionAssetsManagerChromeOS::kSharedExtensionUsers, | 
|  | std::move(users)); | 
|  | extension_info_weak->SetWithoutPathExpansion(version, | 
|  | std::move(version_info)); | 
|  | } | 
|  |  | 
|  | scoped_refptr<const Extension> CreateExtension(const std::string& id, | 
|  | const std::string& version, | 
|  | const base::FilePath& path) { | 
|  | return ExtensionBuilder("test") | 
|  | .SetVersion(version) | 
|  | .SetID(id) | 
|  | .SetPath(path) | 
|  | .SetLocation(mojom::ManifestLocation::kInternal) | 
|  | .Build(); | 
|  | } | 
|  |  | 
|  | ExtensionPrefs* GetExtensionPrefs() { | 
|  | return ExtensionPrefs::Get(profile_.get()); | 
|  | } | 
|  |  | 
|  | ash::FakeChromeUserManager* GetFakeUserManager() { | 
|  | return static_cast<ash::FakeChromeUserManager*>( | 
|  | user_manager::UserManager::Get()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_; | 
|  | base::ScopedTempDir cache_dir_; | 
|  | }; | 
|  |  | 
|  | // Test shared extensions clean up. | 
|  | TEST_F(ExtensionGarbageCollectorChromeOSUnitTest, SharedExtensions) { | 
|  | // Version for non-existing user. | 
|  | base::FilePath path_id1_1 = CreateSharedExtensionDir( | 
|  | kExtensionId1, "1.0", cache_dir()); | 
|  | CreateSharedExtensionPrefs(kExtensionId1, "1.0", "test@test.com", path_id1_1); | 
|  | EXPECT_TRUE(base::PathExists(path_id1_1)); | 
|  |  | 
|  | // Version for current user but the extension is not installed. | 
|  | base::FilePath path_id1_2 = CreateSharedExtensionDir( | 
|  | kExtensionId1, "2.0", cache_dir()); | 
|  | CreateSharedExtensionPrefs(kExtensionId1, "2.0", | 
|  | user_manager::StubAccountId().GetUserEmail(), | 
|  | path_id1_2); | 
|  | EXPECT_TRUE(base::PathExists(path_id1_2)); | 
|  |  | 
|  | // Version for current user that delayed install. | 
|  | base::FilePath path_id2_1 = CreateSharedExtensionDir( | 
|  | kExtensionId2, "1.0", cache_dir()); | 
|  | CreateSharedExtensionPrefs(kExtensionId2, "1.0", | 
|  | user_manager::StubAccountId().GetUserEmail(), | 
|  | path_id2_1); | 
|  | scoped_refptr<const Extension> extension2 = | 
|  | CreateExtension(kExtensionId2, "1.0", path_id2_1); | 
|  | GetExtensionPrefs()->SetDelayedInstallInfo( | 
|  | extension2.get(), | 
|  | Extension::ENABLED, | 
|  | kInstallFlagNone, | 
|  | ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE, | 
|  | syncer::StringOrdinal(), | 
|  | std::string()); | 
|  | EXPECT_TRUE(base::PathExists(path_id2_1)); | 
|  |  | 
|  | GarbageCollectExtensions(); | 
|  |  | 
|  | EXPECT_FALSE(base::PathExists(path_id1_1)); | 
|  | EXPECT_FALSE(base::PathExists(path_id1_2)); | 
|  | EXPECT_FALSE(base::PathExists(cache_dir().AppendASCII(kExtensionId1))); | 
|  |  | 
|  | EXPECT_TRUE(base::PathExists(path_id2_1)); | 
|  |  | 
|  | const base::DictionaryValue* shared_extensions = testing_local_state_.Get()-> | 
|  | GetDictionary(ExtensionAssetsManagerChromeOS::kSharedExtensions); | 
|  | ASSERT_TRUE(shared_extensions); | 
|  |  | 
|  | EXPECT_FALSE(shared_extensions->HasKey(kExtensionId1)); | 
|  | EXPECT_TRUE(shared_extensions->HasKey(kExtensionId2)); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |