Index: chrome/browser/extensions/extension_service_sync_unittest.cc |
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b56d63399dea1b28b9cf73d0657bf190bb61e8b8 |
--- /dev/null |
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc |
@@ -0,0 +1,1719 @@ |
+// Copyright 2015 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 <string> |
+ |
+#include "base/bind.h" |
+#include "base/command_line.h" |
+#include "base/files/file_util.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/metrics/field_trial.h" |
+#include "base/test/mock_entropy_provider.h" |
+#include "chrome/browser/extensions/component_loader.h" |
+#include "chrome/browser/extensions/extension_service.h" |
+#include "chrome/browser/extensions/extension_service_test_with_install.h" |
+#include "chrome/browser/extensions/extension_sync_data.h" |
+#include "chrome/browser/extensions/extension_sync_service.h" |
+#include "chrome/browser/extensions/extension_util.h" |
+#include "chrome/browser/extensions/updater/extension_updater.h" |
+#include "chrome/browser/sync/profile_sync_service.h" |
+#include "chrome/browser/sync/profile_sync_service_factory.h" |
+#include "chrome/common/chrome_constants.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/extensions/sync_helper.h" |
+#include "chrome/test/base/testing_profile.h" |
+#include "extensions/browser/app_sorting.h" |
+#include "extensions/browser/extension_prefs.h" |
+#include "extensions/browser/extension_registry.h" |
+#include "extensions/browser/extension_system.h" |
+#include "extensions/browser/management_policy.h" |
+#include "extensions/browser/test_management_policy.h" |
+#include "extensions/common/constants.h" |
+#include "extensions/common/manifest_url_handlers.h" |
+#include "extensions/common/permissions/permission_set.h" |
+#include "sync/api/fake_sync_change_processor.h" |
+#include "sync/api/sync_data.h" |
+#include "sync/api/sync_error_factory_mock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+#if defined(ENABLE_SUPERVISED_USERS) |
+#include "chrome/browser/supervised_user/permission_request_creator.h" |
+#include "chrome/browser/supervised_user/supervised_user_constants.h" |
+#include "chrome/browser/supervised_user/supervised_user_service.h" |
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h" |
+#endif |
+ |
+using extensions::AppSorting; |
+using extensions::Extension; |
+using extensions::ExtensionPrefs; |
+using extensions::ExtensionSyncData; |
+using extensions::ExtensionSystem; |
+using extensions::Manifest; |
+using extensions::PermissionSet; |
+ |
+const char good0[] = "behllobkkfkfnphdnhnkndlbkcpglgmj"; |
+const char good2[] = "bjafgdebaacbbbecmhlhpofkepfkgcpa"; |
+const char good_crx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; |
+const char page_action[] = "obcimlgaoabeegjmmpldobjndiealpln"; |
+const char theme2_crx[] = "pjpgmfcmabopnnfonnhmdjglfpjjfkbf"; |
+ |
+class ExtensionServiceSyncTest |
+ : public extensions::ExtensionServiceTestWithInstall { |
+ public: |
+ void MockSyncStartFlare(bool* was_called, |
+ syncer::ModelType* model_type_passed_in, |
+ syncer::ModelType model_type) { |
+ *was_called = true; |
+ *model_type_passed_in = model_type; |
+ } |
+ |
+ protected: |
+ // Paths to some of the fake extensions. |
+ base::FilePath good0_path() { |
+ return data_dir() |
+ .AppendASCII("good") |
+ .AppendASCII("Extensions") |
+ .AppendASCII(good0) |
+ .AppendASCII("1.0.0.0"); |
+ } |
+ |
+ ExtensionSyncService* extension_sync_service() { |
+ return ExtensionSyncService::Get(profile()); |
+ } |
+}; |
+ |
+TEST_F(ExtensionServiceSyncTest, DeferredSyncStartupPreInstalledComponent) { |
+ InitializeEmptyExtensionService(); |
+ |
+ bool flare_was_called = false; |
+ syncer::ModelType triggered_type(syncer::UNSPECIFIED); |
+ base::WeakPtrFactory<ExtensionServiceSyncTest> factory(this); |
+ extension_sync_service()->SetSyncStartFlareForTesting( |
+ base::Bind(&ExtensionServiceSyncTest::MockSyncStartFlare, |
+ factory.GetWeakPtr(), |
+ &flare_was_called, // Safe due to WeakPtrFactory scope. |
+ &triggered_type)); // Safe due to WeakPtrFactory scope. |
+ |
+ // Install a component extension. |
+ std::string manifest; |
+ ASSERT_TRUE(base::ReadFileToString( |
+ good0_path().Append(extensions::kManifestFilename), &manifest)); |
+ service()->component_loader()->Add(manifest, good0_path()); |
+ ASSERT_FALSE(service()->is_ready()); |
+ service()->Init(); |
+ ASSERT_TRUE(service()->is_ready()); |
+ |
+ // Extensions added before service is_ready() don't trigger sync startup. |
+ EXPECT_FALSE(flare_was_called); |
+ ASSERT_EQ(syncer::UNSPECIFIED, triggered_type); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, DeferredSyncStartupPreInstalledNormal) { |
+ InitializeGoodInstalledExtensionService(); |
+ |
+ bool flare_was_called = false; |
+ syncer::ModelType triggered_type(syncer::UNSPECIFIED); |
+ base::WeakPtrFactory<ExtensionServiceSyncTest> factory(this); |
+ extension_sync_service()->SetSyncStartFlareForTesting( |
+ base::Bind(&ExtensionServiceSyncTest::MockSyncStartFlare, |
+ factory.GetWeakPtr(), |
+ &flare_was_called, // Safe due to WeakPtrFactory scope. |
+ &triggered_type)); // Safe due to WeakPtrFactory scope. |
+ |
+ ASSERT_FALSE(service()->is_ready()); |
+ service()->Init(); |
+ ASSERT_EQ(3u, loaded_.size()); |
+ ASSERT_TRUE(service()->is_ready()); |
+ |
+ // Extensions added before service is_ready() don't trigger sync startup. |
+ EXPECT_FALSE(flare_was_called); |
+ ASSERT_EQ(syncer::UNSPECIFIED, triggered_type); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, DeferredSyncStartupOnInstall) { |
+ InitializeEmptyExtensionService(); |
+ service()->Init(); |
+ ASSERT_TRUE(service()->is_ready()); |
+ |
+ bool flare_was_called = false; |
+ syncer::ModelType triggered_type(syncer::UNSPECIFIED); |
+ base::WeakPtrFactory<ExtensionServiceSyncTest> factory(this); |
+ extension_sync_service()->SetSyncStartFlareForTesting( |
+ base::Bind(&ExtensionServiceSyncTest::MockSyncStartFlare, |
+ factory.GetWeakPtr(), |
+ &flare_was_called, // Safe due to WeakPtrFactory scope. |
+ &triggered_type)); // Safe due to WeakPtrFactory scope. |
+ |
+ base::FilePath path = data_dir().AppendASCII("good.crx"); |
+ InstallCRX(path, INSTALL_NEW); |
+ |
+ EXPECT_TRUE(flare_was_called); |
+ EXPECT_EQ(syncer::EXTENSIONS, triggered_type); |
+ |
+ // Reset. |
+ flare_was_called = false; |
+ triggered_type = syncer::UNSPECIFIED; |
+ |
+ // Once sync starts, flare should no longer be invoked. |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ path = data_dir().AppendASCII("page_action.crx"); |
+ InstallCRX(path, INSTALL_NEW); |
+ EXPECT_FALSE(flare_was_called); |
+ ASSERT_EQ(syncer::UNSPECIFIED, triggered_type); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, DisableExtensionFromSync) { |
+ // Start the extensions service with one external extension already installed. |
+ base::FilePath source_install_dir = |
+ data_dir().AppendASCII("good").AppendASCII("Extensions"); |
+ base::FilePath pref_path = |
+ source_install_dir.DirName().Append(chrome::kPreferencesFilename); |
+ |
+ InitializeInstalledExtensionService(pref_path, source_install_dir); |
+ |
+ // The user has enabled sync. |
+ ProfileSyncService* sync_service = |
+ ProfileSyncServiceFactory::GetForProfile(profile()); |
+ sync_service->SetSyncSetupCompleted(); |
+ |
+ service()->Init(); |
+ ASSERT_TRUE(service()->is_ready()); |
+ |
+ ASSERT_EQ(3u, loaded_.size()); |
+ |
+ // We start enabled. |
+ const Extension* extension = service()->GetExtensionById(good0, true); |
+ ASSERT_TRUE(extension); |
+ ASSERT_TRUE(service()->IsExtensionEnabled(good0)); |
+ |
+ // Sync starts up. |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ make_scoped_ptr(new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ // Then sync data arrives telling us to disable |good0|. |
+ ExtensionSyncData disable_good_crx(*extension, false, |
+ Extension::DISABLE_USER_ACTION, false, |
+ false, ExtensionSyncData::BOOLEAN_UNSET); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ disable_good_crx.GetSyncData()); |
+ syncer::SyncChangeList list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ |
+ ASSERT_FALSE(service()->IsExtensionEnabled(good0)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, IgnoreSyncChangesWhenLocalStateIsMoreRecent) { |
+ // Start the extension service with three extensions already installed. |
+ base::FilePath source_install_dir = |
+ data_dir().AppendASCII("good").AppendASCII("Extensions"); |
+ base::FilePath pref_path = |
+ source_install_dir.DirName().Append(chrome::kPreferencesFilename); |
+ |
+ InitializeInstalledExtensionService(pref_path, source_install_dir); |
+ |
+ // The user has enabled sync. |
+ ProfileSyncService* sync_service = |
+ ProfileSyncServiceFactory::GetForProfile(profile()); |
+ sync_service->SetSyncSetupCompleted(); |
+ // Make sure ExtensionSyncService is created, so it'll be notified of changes. |
+ extension_sync_service(); |
+ |
+ service()->Init(); |
+ ASSERT_TRUE(service()->is_ready()); |
+ ASSERT_EQ(3u, loaded_.size()); |
+ |
+ ASSERT_TRUE(service()->IsExtensionEnabled(good0)); |
+ ASSERT_TRUE(service()->IsExtensionEnabled(good2)); |
+ |
+ // Disable and re-enable good0 before first sync data arrives. |
+ service()->DisableExtension(good0, Extension::DISABLE_USER_ACTION); |
+ ASSERT_FALSE(service()->IsExtensionEnabled(good0)); |
+ service()->EnableExtension(good0); |
+ ASSERT_TRUE(service()->IsExtensionEnabled(good0)); |
+ // Disable good2 before first sync data arrives (good1 is considered |
+ // non-syncable because it has plugin permission). |
+ service()->DisableExtension(good2, Extension::DISABLE_USER_ACTION); |
+ ASSERT_FALSE(service()->IsExtensionEnabled(good2)); |
+ |
+ const Extension* extension0 = service()->GetExtensionById(good0, true); |
+ const Extension* extension2 = service()->GetExtensionById(good2, true); |
+ ASSERT_TRUE(extensions::sync_helper::IsSyncable(extension0)); |
+ ASSERT_TRUE(extensions::sync_helper::IsSyncable(extension2)); |
+ |
+ // Now sync data comes in that says to disable good0 and enable good2. |
+ ExtensionSyncData disable_good0(*extension0, false, |
+ Extension::DISABLE_USER_ACTION, false, false, |
+ ExtensionSyncData::BOOLEAN_UNSET); |
+ ExtensionSyncData enable_good2(*extension2, true, Extension::DISABLE_NONE, |
+ false, false, |
+ ExtensionSyncData::BOOLEAN_UNSET); |
+ syncer::SyncDataList sync_data; |
+ sync_data.push_back(disable_good0.GetSyncData()); |
+ sync_data.push_back(enable_good2.GetSyncData()); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ sync_data, |
+ make_scoped_ptr(new syncer::FakeSyncChangeProcessor), |
+ make_scoped_ptr(new syncer::SyncErrorFactoryMock)); |
+ |
+ // Both sync changes should be ignored, since the local state was changed |
+ // before sync started, and so the local state is considered more recent. |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good0)); |
+ EXPECT_FALSE(service()->IsExtensionEnabled(good2)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncData) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ const Extension* extension = service()->GetInstalledExtension(good_crx); |
+ ASSERT_TRUE(extension); |
+ |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_EQ(extension->id(), data->id()); |
+ EXPECT_FALSE(data->uninstalled()); |
+ EXPECT_EQ(service()->IsExtensionEnabled(good_crx), data->enabled()); |
+ EXPECT_EQ(extensions::util::IsIncognitoEnabled(good_crx, profile()), |
+ data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data->all_urls_enabled()); |
+ EXPECT_TRUE(data->version().Equals(*extension->version())); |
+ EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension), |
+ data->update_url()); |
+ EXPECT_EQ(extension->name(), data->name()); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncDataTerminated) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ TerminateExtension(good_crx); |
+ const Extension* extension = service()->GetInstalledExtension(good_crx); |
+ ASSERT_TRUE(extension); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_EQ(extension->id(), data->id()); |
+ EXPECT_FALSE(data->uninstalled()); |
+ EXPECT_EQ(service()->IsExtensionEnabled(good_crx), data->enabled()); |
+ EXPECT_EQ(extensions::util::IsIncognitoEnabled(good_crx, profile()), |
+ data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data->all_urls_enabled()); |
+ EXPECT_TRUE(data->version().Equals(*extension->version())); |
+ EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension), |
+ data->update_url()); |
+ EXPECT_EQ(extension->name(), data->name()); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncDataFilter) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ const Extension* extension = service()->GetInstalledExtension(good_crx); |
+ ASSERT_TRUE(extension); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::APPS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 0U); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncExtensionDataUserSettings) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ const Extension* extension = service()->GetInstalledExtension(good_crx); |
+ ASSERT_TRUE(extension); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_TRUE(data->enabled()); |
+ EXPECT_FALSE(data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data->all_urls_enabled()); |
+ } |
+ |
+ service()->DisableExtension(good_crx, Extension::DISABLE_USER_ACTION); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_FALSE(data->enabled()); |
+ EXPECT_FALSE(data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data->all_urls_enabled()); |
+ } |
+ |
+ extensions::util::SetIsIncognitoEnabled(good_crx, profile(), true); |
+ extensions::util::SetAllowedScriptingOnAllUrls( |
+ good_crx, profile(), false); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_FALSE(data->enabled()); |
+ EXPECT_TRUE(data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_FALSE, data->all_urls_enabled()); |
+ } |
+ |
+ service()->EnableExtension(good_crx); |
+ extensions::util::SetAllowedScriptingOnAllUrls( |
+ good_crx, profile(), true); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(list.size(), 1U); |
+ scoped_ptr<ExtensionSyncData> data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(data.get()); |
+ EXPECT_TRUE(data->enabled()); |
+ EXPECT_TRUE(data->incognito_enabled()); |
+ EXPECT_EQ(ExtensionSyncData::BOOLEAN_TRUE, data->all_urls_enabled()); |
+ } |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, SyncForUninstalledExternalExtension) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRXWithLocation( |
+ data_dir().AppendASCII("good.crx"), Manifest::EXTERNAL_PREF, INSTALL_NEW); |
+ const Extension* extension = service()->GetInstalledExtension(good_crx); |
+ ASSERT_TRUE(extension); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ UninstallExtension(good_crx, false); |
+ EXPECT_TRUE( |
+ ExtensionPrefs::Get(profile())->IsExternalExtensionUninstalled(good_crx)); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::AppSpecifics* app_specifics = specifics.mutable_app(); |
+ sync_pb::ExtensionSpecifics* extension_specifics = |
+ app_specifics->mutable_extension(); |
+ extension_specifics->set_id(good_crx); |
+ extension_specifics->set_version("1.0"); |
+ extension_specifics->set_enabled(true); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE( |
+ ExtensionPrefs::Get(profile())->IsExternalExtensionUninstalled(good_crx)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncAppDataUserSettings) { |
+ InitializeEmptyExtensionService(); |
+ const Extension* app = |
+ PackAndInstallCRX(data_dir().AppendASCII("app"), INSTALL_NEW); |
+ ASSERT_TRUE(app); |
+ ASSERT_TRUE(app->is_app()); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::APPS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ syncer::StringOrdinal initial_ordinal = |
+ syncer::StringOrdinal::CreateInitialOrdinal(); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::APPS); |
+ ASSERT_EQ(list.size(), 1U); |
+ |
+ scoped_ptr<ExtensionSyncData> app_sync_data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->app_launch_ordinal())); |
+ EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->page_ordinal())); |
+ } |
+ |
+ AppSorting* sorting = ExtensionPrefs::Get(profile())->app_sorting(); |
+ sorting->SetAppLaunchOrdinal(app->id(), initial_ordinal.CreateAfter()); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::APPS); |
+ ASSERT_EQ(list.size(), 1U); |
+ |
+ scoped_ptr<ExtensionSyncData> app_sync_data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(app_sync_data.get()); |
+ EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->app_launch_ordinal())); |
+ EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->page_ordinal())); |
+ } |
+ |
+ sorting->SetPageOrdinal(app->id(), initial_ordinal.CreateAfter()); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::APPS); |
+ ASSERT_EQ(list.size(), 1U); |
+ |
+ scoped_ptr<ExtensionSyncData> app_sync_data = |
+ ExtensionSyncData::CreateFromSyncData(list[0]); |
+ ASSERT_TRUE(app_sync_data.get()); |
+ EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->app_launch_ordinal())); |
+ EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->page_ordinal())); |
+ } |
+} |
+ |
+// TODO (rdevlin.cronin): The OnExtensionMoved() method has been removed from |
+// ExtensionService, so this test probably needs a new home. Unfortunately, it |
+// relies pretty heavily on things like InitializeExtension[Sync]Service() and |
+// PackAndInstallCRX(). When we clean up a bit more, this should move out. |
+TEST_F(ExtensionServiceSyncTest, GetSyncAppDataUserSettingsOnExtensionMoved) { |
+ InitializeEmptyExtensionService(); |
+ const size_t kAppCount = 3; |
+ const Extension* apps[kAppCount]; |
+ apps[0] = PackAndInstallCRX(data_dir().AppendASCII("app1"), INSTALL_NEW); |
+ apps[1] = PackAndInstallCRX(data_dir().AppendASCII("app2"), INSTALL_NEW); |
+ apps[2] = PackAndInstallCRX(data_dir().AppendASCII("app4"), INSTALL_NEW); |
+ for (size_t i = 0; i < kAppCount; ++i) { |
+ ASSERT_TRUE(apps[i]); |
+ ASSERT_TRUE(apps[i]->is_app()); |
+ } |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::APPS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ ExtensionPrefs::Get(service()->GetBrowserContext()) |
+ ->app_sorting() |
+ ->OnExtensionMoved(apps[0]->id(), apps[1]->id(), apps[2]->id()); |
+ { |
+ syncer::SyncDataList list = |
+ extension_sync_service()->GetAllSyncData(syncer::APPS); |
+ ASSERT_EQ(list.size(), 3U); |
+ |
+ scoped_ptr<ExtensionSyncData> data[kAppCount]; |
+ for (size_t i = 0; i < kAppCount; ++i) { |
+ data[i] = ExtensionSyncData::CreateFromSyncData(list[i]); |
+ ASSERT_TRUE(data[i].get()); |
+ } |
+ |
+ // The sync data is not always in the same order our apps were installed in, |
+ // so we do that sorting here so we can make sure the values are changed as |
+ // expected. |
+ syncer::StringOrdinal app_launch_ordinals[kAppCount]; |
+ for (size_t i = 0; i < kAppCount; ++i) { |
+ for (size_t j = 0; j < kAppCount; ++j) { |
+ if (apps[i]->id() == data[j]->id()) |
+ app_launch_ordinals[i] = data[j]->app_launch_ordinal(); |
+ } |
+ } |
+ |
+ EXPECT_TRUE(app_launch_ordinals[1].LessThan(app_launch_ordinals[0])); |
+ EXPECT_TRUE(app_launch_ordinals[0].LessThan(app_launch_ordinals[2])); |
+ } |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, GetSyncDataList) { |
+ InitializeEmptyExtensionService(); |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ InstallCRX(data_dir().AppendASCII("page_action.crx"), INSTALL_NEW); |
+ InstallCRX(data_dir().AppendASCII("theme.crx"), INSTALL_NEW); |
+ InstallCRX(data_dir().AppendASCII("theme2.crx"), INSTALL_NEW); |
+ |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::APPS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ service()->DisableExtension(page_action, Extension::DISABLE_USER_ACTION); |
+ TerminateExtension(theme2_crx); |
+ |
+ EXPECT_EQ(0u, extension_sync_service()->GetAllSyncData(syncer::APPS).size()); |
+ EXPECT_EQ( |
+ 2u, extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS).size()); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataUninstall) { |
+ InitializeEmptyExtensionService(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_version("1.0"); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_DELETE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ // Should do nothing. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->GetExtensionById(good_crx, true)); |
+ |
+ // Install the extension. |
+ base::FilePath extension_path = data_dir().AppendASCII("good.crx"); |
+ InstallCRX(extension_path, INSTALL_NEW); |
+ EXPECT_TRUE(service()->GetExtensionById(good_crx, true)); |
+ |
+ // Should uninstall the extension. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->GetExtensionById(good_crx, true)); |
+ |
+ // Should again do nothing. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->GetExtensionById(good_crx, true)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataWrongType) { |
+ InitializeEmptyExtensionService(); |
+ |
+ // Install the extension. |
+ base::FilePath extension_path = data_dir().AppendASCII("good.crx"); |
+ InstallCRX(extension_path, INSTALL_NEW); |
+ EXPECT_TRUE(service()->GetExtensionById(good_crx, true)); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::AppSpecifics* app_specifics = specifics.mutable_app(); |
+ sync_pb::ExtensionSpecifics* extension_specifics = |
+ app_specifics->mutable_extension(); |
+ extension_specifics->set_id(good_crx); |
+ extension_specifics->set_version( |
+ service()->GetInstalledExtension(good_crx)->version()->GetString()); |
+ |
+ { |
+ extension_specifics->set_enabled(true); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_DELETE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ // Should do nothing |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->GetExtensionById(good_crx, true)); |
+ } |
+ |
+ { |
+ extension_specifics->set_enabled(false); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ // Should again do nothing. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->GetExtensionById(good_crx, false)); |
+ } |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataSettings) { |
+ InitializeEmptyExtensionService(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ EXPECT_FALSE(extensions::util::HasSetAllowedScriptingOnAllUrls( |
+ good_crx, profile())); |
+ const bool kDefaultAllowedScripting = |
+ extensions::util::DefaultAllowedScriptingOnAllUrls(); |
+ EXPECT_EQ(kDefaultAllowedScripting, |
+ extensions::util::AllowedScriptingOnAllUrls(good_crx, profile())); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_version( |
+ service()->GetInstalledExtension(good_crx)->version()->GetString()); |
+ ext_specifics->set_enabled(false); |
+ |
+ { |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ EXPECT_FALSE(extensions::util::HasSetAllowedScriptingOnAllUrls( |
+ good_crx, profile())); |
+ EXPECT_EQ(kDefaultAllowedScripting, |
+ extensions::util::AllowedScriptingOnAllUrls(good_crx, profile())); |
+ } |
+ |
+ { |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_incognito_enabled(true); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ } |
+ |
+ { |
+ ext_specifics->set_enabled(false); |
+ ext_specifics->set_incognito_enabled(true); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ } |
+ |
+ { |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_all_urls_enabled(!kDefaultAllowedScripting); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::HasSetAllowedScriptingOnAllUrls( |
+ good_crx, profile())); |
+ EXPECT_EQ(!kDefaultAllowedScripting, |
+ extensions::util::AllowedScriptingOnAllUrls(good_crx, profile())); |
+ } |
+ |
+ { |
+ ext_specifics->set_all_urls_enabled(kDefaultAllowedScripting); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::HasSetAllowedScriptingOnAllUrls( |
+ good_crx, profile())); |
+ EXPECT_EQ(kDefaultAllowedScripting, |
+ extensions::util::AllowedScriptingOnAllUrls(good_crx, profile())); |
+ } |
+ |
+ EXPECT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataNewExtension) { |
+ InitializeEmptyExtensionService(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ const base::FilePath path = data_dir().AppendASCII("good.crx"); |
+ const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
+ |
+ struct TestCase { |
+ const char* name; // For failure output only. |
+ bool sync_enabled; // The "enabled" flag coming in from Sync. |
+ // The disable reason(s) coming in from Sync, or -1 for "not set". |
+ int sync_disable_reasons; |
+ // The disable reason(s) that should be set on the installed extension. |
+ // This will usually be the same as |sync_disable_reasons|, but see the |
+ // "Legacy" case. |
+ int expect_disable_reasons; |
+ // Whether the extension's permissions should be auto-granted during |
+ // installation. |
+ bool expect_permissions_granted; |
+ } test_cases[] = { |
+ // Standard case: Extension comes in enabled; permissions should be granted |
+ // during installation. |
+ { "Standard", true, 0, 0, true }, |
+ // If the extension comes in disabled, its permissions should still be |
+ // granted (the user already approved them on another machine). |
+ { "Disabled", false, Extension::DISABLE_USER_ACTION, |
+ Extension::DISABLE_USER_ACTION, true }, |
+ // Legacy case (<M45): No disable reasons come in from Sync (see |
+ // crbug.com/484214). After installation, the reason should be set to |
+ // DISABLE_UNKNOWN_FROM_SYNC. |
+ { "Legacy", false, -1, Extension::DISABLE_UNKNOWN_FROM_SYNC, true }, |
+ // If the extension came in disabled due to a permissions increase, then the |
+ // user has *not* approved the permissions, and they shouldn't be granted. |
+ // crbug.com/484214 |
+ { "PermissionsIncrease", false, Extension::DISABLE_PERMISSIONS_INCREASE, |
+ Extension::DISABLE_PERMISSIONS_INCREASE, false }, |
+ }; |
+ |
+ for (const TestCase& test_case : test_cases) { |
+ SCOPED_TRACE(test_case.name); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_version(base::Version("1").GetString()); |
+ ext_specifics->set_enabled(test_case.sync_enabled); |
+ if (test_case.sync_disable_reasons != -1) |
+ ext_specifics->set_disable_reasons(test_case.sync_disable_reasons); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ |
+ ASSERT_TRUE(service()->pending_extension_manager()->IsIdPending(good_crx)); |
+ UpdateExtension(good_crx, path, test_case.sync_enabled ? ENABLED |
+ : DISABLED); |
+ EXPECT_EQ(test_case.expect_disable_reasons, |
+ prefs->GetDisableReasons(good_crx)); |
+ scoped_ptr<const PermissionSet> permissions = |
+ prefs->GetGrantedPermissions(good_crx); |
+ EXPECT_EQ(test_case.expect_permissions_granted, !permissions->IsEmpty()); |
+ ASSERT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx)); |
+ |
+ // Remove the extension again, so we can install it again for the next case. |
+ UninstallExtension(good_crx, false, |
+ test_case.sync_enabled ? Extension::ENABLED |
+ : Extension::DISABLED); |
+ } |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataTerminatedExtension) { |
+ InitializeExtensionServiceWithUpdater(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ TerminateExtension(good_crx); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_version( |
+ service()->GetInstalledExtension(good_crx)->version()->GetString()); |
+ ext_specifics->set_enabled(false); |
+ ext_specifics->set_incognito_enabled(true); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ |
+ EXPECT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataVersionCheck) { |
+ InitializeExtensionServiceWithUpdater(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_enabled(true); |
+ |
+ const base::Version installed_version = |
+ *service()->GetInstalledExtension(good_crx)->version(); |
+ |
+ { |
+ ext_specifics->set_version(installed_version.GetString()); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ |
+ // Should do nothing if extension version == sync version. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->updater()->WillCheckSoon()); |
+ // Make sure the version we'll send back to sync didn't change. |
+ syncer::SyncDataList data = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(1u, data.size()); |
+ scoped_ptr<ExtensionSyncData> extension_data = |
+ ExtensionSyncData::CreateFromSyncData(data[0]); |
+ ASSERT_TRUE(extension_data); |
+ EXPECT_TRUE(installed_version.Equals(extension_data->version())); |
+ } |
+ |
+ // Should do nothing if extension version > sync version. |
+ { |
+ ext_specifics->set_version("0.0.0.0"); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_FALSE(service()->updater()->WillCheckSoon()); |
+ // Make sure the version we'll send back to sync didn't change. |
+ syncer::SyncDataList data = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(1u, data.size()); |
+ scoped_ptr<ExtensionSyncData> extension_data = |
+ ExtensionSyncData::CreateFromSyncData(data[0]); |
+ ASSERT_TRUE(extension_data); |
+ EXPECT_TRUE(installed_version.Equals(extension_data->version())); |
+ } |
+ |
+ // Should kick off an update if extension version < sync version. |
+ { |
+ const base::Version new_version("9.9.9.9"); |
+ ext_specifics->set_version(new_version.GetString()); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->updater()->WillCheckSoon()); |
+ // Make sure that we'll send the NEW version back to sync, even though we |
+ // haven't actually updated yet. This is to prevent the data in sync from |
+ // flip-flopping back and forth until all clients are up to date. |
+ syncer::SyncDataList data = |
+ extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS); |
+ ASSERT_EQ(1u, data.size()); |
+ scoped_ptr<ExtensionSyncData> extension_data = |
+ ExtensionSyncData::CreateFromSyncData(data[0]); |
+ ASSERT_TRUE(extension_data); |
+ EXPECT_TRUE(new_version.Equals(extension_data->version())); |
+ } |
+ |
+ EXPECT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx)); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataNotInstalled) { |
+ InitializeExtensionServiceWithUpdater(); |
+ syncer::FakeSyncChangeProcessor processor; |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(good_crx); |
+ ext_specifics->set_enabled(false); |
+ ext_specifics->set_incognito_enabled(true); |
+ ext_specifics->set_update_url("http://www.google.com/"); |
+ ext_specifics->set_version("1.2.3.4"); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1); |
+ list[0] = sync_change; |
+ |
+ EXPECT_TRUE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ EXPECT_TRUE(service()->updater()->WillCheckSoon()); |
+ EXPECT_FALSE(service()->IsExtensionEnabled(good_crx)); |
+ EXPECT_TRUE(extensions::util::IsIncognitoEnabled(good_crx, profile())); |
+ |
+ const extensions::PendingExtensionInfo* info; |
+ EXPECT_TRUE( |
+ (info = service()->pending_extension_manager()->GetById(good_crx))); |
+ EXPECT_EQ(ext_specifics->update_url(), info->update_url().spec()); |
+ EXPECT_TRUE(info->is_from_sync()); |
+ EXPECT_EQ(Manifest::INTERNAL, info->install_source()); |
+ // TODO(akalin): Figure out a way to test |info.ShouldAllowInstall()|. |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataEnableDisable) { |
+ InitializeEmptyExtensionService(); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
+ |
+ struct TestCase { |
+ const char* name; // For failure output only. |
+ // Set of disable reasons before any Sync data comes in. If this is != 0, |
+ // the extension is disabled. |
+ int previous_disable_reasons; |
+ bool sync_enable; // The enabled flag coming in from Sync. |
+ // The disable reason(s) coming in from Sync, or -1 for "not set". |
+ int sync_disable_reasons; |
+ // The expected set of disable reasons after processing the Sync update. The |
+ // extension should be disabled iff this is != 0. |
+ int expect_disable_reasons; |
+ } test_cases[] = { |
+ { "NopEnable", 0, true, 0, 0 }, |
+ { "NopDisable", Extension::DISABLE_USER_ACTION, false, |
+ Extension::DISABLE_USER_ACTION, Extension::DISABLE_USER_ACTION }, |
+ { "Disable", 0, false, Extension::DISABLE_USER_ACTION, |
+ Extension::DISABLE_USER_ACTION }, |
+ { "DisableLegacy", 0, false, -1, Extension::DISABLE_UNKNOWN_FROM_SYNC }, |
+ { "AddDisableReason", Extension::DISABLE_REMOTE_INSTALL, false, |
+ Extension::DISABLE_REMOTE_INSTALL | Extension::DISABLE_USER_ACTION, |
+ Extension::DISABLE_REMOTE_INSTALL | Extension::DISABLE_USER_ACTION }, |
+ { "AddDisableReasonLegacy", Extension::DISABLE_USER_ACTION, false, -1, |
+ Extension::DISABLE_USER_ACTION | Extension::DISABLE_UNKNOWN_FROM_SYNC}, |
+ { "RemoveDisableReason", |
+ Extension::DISABLE_REMOTE_INSTALL | Extension::DISABLE_USER_ACTION, false, |
+ Extension::DISABLE_USER_ACTION, Extension::DISABLE_USER_ACTION }, |
+ { "Enable", Extension::DISABLE_USER_ACTION, true, 0, 0 }, |
+ { "EnableLegacy", Extension::DISABLE_USER_ACTION, true, -1, 0 }, |
+ }; |
+ |
+ for (const TestCase& test_case : test_cases) { |
+ SCOPED_TRACE(test_case.name); |
+ |
+ std::string id; |
+ std::string version; |
+ // Don't keep |extension| around longer than necessary. |
+ { |
+ const Extension* extension = |
+ InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW); |
+ // The extension should now be installed and enabled. |
+ ASSERT_TRUE(extension); |
+ id = extension->id(); |
+ version = extension->VersionString(); |
+ } |
+ ASSERT_TRUE(registry()->enabled_extensions().Contains(id)); |
+ |
+ // Disable it if the test case says so. |
+ if (test_case.previous_disable_reasons) { |
+ service()->DisableExtension(id, test_case.previous_disable_reasons); |
+ ASSERT_TRUE(registry()->disabled_extensions().Contains(id)); |
+ } |
+ |
+ // Now a sync update comes in. |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_enabled(test_case.sync_enable); |
+ ext_specifics->set_version(version); |
+ if (test_case.sync_disable_reasons != -1) |
+ ext_specifics->set_disable_reasons(test_case.sync_disable_reasons); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ |
+ // Check expectations. |
+ const bool expect_enabled = !test_case.expect_disable_reasons; |
+ EXPECT_EQ(expect_enabled, service()->IsExtensionEnabled(id)); |
+ EXPECT_EQ(test_case.expect_disable_reasons, prefs->GetDisableReasons(id)); |
+ |
+ // Remove the extension again, so we can install it again for the next case. |
+ UninstallExtension(id, false, expect_enabled ? Extension::ENABLED |
+ : Extension::DISABLED); |
+ } |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataDeferredEnable) { |
+ InitializeEmptyExtensionService(); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); |
+ base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
+ |
+ base::FilePath path = base_path.AppendASCII("v1"); |
+ const Extension* extension = PackAndInstallCRX(path, pem_path, INSTALL_NEW); |
+ // The extension must now be installed and enabled. |
+ ASSERT_TRUE(extension); |
+ ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
+ |
+ // Save the id, as the extension object will be destroyed during updating. |
+ std::string id = extension->id(); |
+ |
+ // Update to a new version with increased permissions. |
+ path = base_path.AppendASCII("v2"); |
+ PackCRXAndUpdateExtension(id, path, pem_path, DISABLED); |
+ |
+ // Now a sync update comes in, telling us to re-enable a *newer* version. |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_version("3"); |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_disable_reasons(Extension::DISABLE_NONE); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ |
+ // Since the version didn't match, the extension should still be disabled. |
+ EXPECT_TRUE(registry()->disabled_extensions().Contains(id)); |
+ |
+ // After we update to the matching version, the extension should get enabled. |
+ path = base_path.AppendASCII("v3"); |
+ PackCRXAndUpdateExtension(id, path, pem_path, ENABLED); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, ProcessSyncDataPermissionApproval) { |
+ // This is the update URL specified in the test extension. Setting it here is |
+ // necessary to make it considered syncable. |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kAppsGalleryUpdateURL, |
+ "http://localhost/autoupdate/updates.xml"); |
+ |
+ InitializeEmptyExtensionService(); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ const base::FilePath base_path = |
+ data_dir().AppendASCII("permissions_increase"); |
+ const base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); |
+ const base::FilePath path_v1 = base_path.AppendASCII("v1"); |
+ const base::FilePath path_v2 = base_path.AppendASCII("v2"); |
+ |
+ base::ScopedTempDir crx_dir; |
+ ASSERT_TRUE(crx_dir.CreateUniqueTempDir()); |
+ const base::FilePath crx_path_v1 = crx_dir.path().AppendASCII("temp1.crx"); |
+ PackCRX(path_v1, pem_path, crx_path_v1); |
+ const base::FilePath crx_path_v2 = crx_dir.path().AppendASCII("temp2.crx"); |
+ PackCRX(path_v2, pem_path, crx_path_v2); |
+ |
+ const std::string v1("1"); |
+ const std::string v2("2"); |
+ |
+ const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
+ |
+ struct TestCase { |
+ const char* name; // For failure output only. |
+ const std::string& sync_version; // The version coming in from Sync. |
+ // The disable reason(s) coming in from Sync, or -1 for "not set". |
+ int sync_disable_reasons; |
+ // The expected set of disable reasons after processing the Sync update. The |
+ // extension should be enabled iff this is 0. |
+ int expect_disable_reasons; |
+ // Whether the extension's permissions should be auto-granted. |
+ bool expect_permissions_granted; |
+ } test_cases[] = { |
+ // Sync tells us to re-enable an older version. No permissions should be |
+ // granted, since we can't be sure if the user actually approved the right |
+ // set of permissions. |
+ { "OldVersion", v1, 0, Extension::DISABLE_PERMISSIONS_INCREASE, false }, |
+ // Legacy case: Sync tells us to re-enable the extension, but doesn't |
+ // specify disable reasons. No permissions should be granted. |
+ { "Legacy", v2, -1, Extension::DISABLE_PERMISSIONS_INCREASE, false }, |
+ // Sync tells us to re-enable the extension and explicitly removes the |
+ // disable reasons. Now the extension should have its permissions granted. |
+ { "GrantPermissions", v2, 0, Extension::DISABLE_NONE, true }, |
+ }; |
+ |
+ for (const TestCase& test_case : test_cases) { |
+ SCOPED_TRACE(test_case.name); |
+ |
+ std::string id; |
+ // Don't keep |extension| around longer than necessary (it'll be destroyed |
+ // during updating). |
+ { |
+ const Extension* extension = InstallCRX(crx_path_v1, INSTALL_NEW); |
+ // The extension should now be installed and enabled. |
+ ASSERT_TRUE(extension); |
+ ASSERT_EQ(v1, extension->VersionString()); |
+ id = extension->id(); |
+ } |
+ ASSERT_TRUE(registry()->enabled_extensions().Contains(id)); |
+ |
+ scoped_ptr<const PermissionSet> granted_permissions_v1 = |
+ prefs->GetGrantedPermissions(id); |
+ |
+ // Update to a new version with increased permissions. |
+ UpdateExtension(id, crx_path_v2, DISABLED); |
+ |
+ // Now the extension should be disabled due to a permissions increase. |
+ { |
+ const Extension* extension = |
+ registry()->disabled_extensions().GetByID(id); |
+ ASSERT_TRUE(extension); |
+ ASSERT_EQ(v2, extension->VersionString()); |
+ } |
+ ASSERT_TRUE(prefs->HasDisableReason( |
+ id, Extension::DISABLE_PERMISSIONS_INCREASE)); |
+ |
+ // No new permissions should have been granted. |
+ scoped_ptr<const PermissionSet> granted_permissions_v2 = |
+ prefs->GetGrantedPermissions(id); |
+ ASSERT_EQ(*granted_permissions_v1, *granted_permissions_v2); |
+ |
+ // Now a sync update comes in. |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_version(test_case.sync_version); |
+ if (test_case.sync_disable_reasons != -1) |
+ ext_specifics->set_disable_reasons(test_case.sync_disable_reasons); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(good_crx, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, |
+ syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, list); |
+ |
+ // Check expectations. |
+ const bool expect_enabled = !test_case.expect_disable_reasons; |
+ EXPECT_EQ(expect_enabled, service()->IsExtensionEnabled(id)); |
+ EXPECT_EQ(test_case.expect_disable_reasons, prefs->GetDisableReasons(id)); |
+ scoped_ptr<const PermissionSet> granted_permissions = |
+ prefs->GetGrantedPermissions(id); |
+ if (test_case.expect_permissions_granted) { |
+ scoped_ptr<const PermissionSet> active_permissions = |
+ prefs->GetActivePermissions(id); |
+ EXPECT_EQ(*granted_permissions, *active_permissions); |
+ } else { |
+ EXPECT_EQ(*granted_permissions, *granted_permissions_v1); |
+ } |
+ |
+ // Remove the extension again, so we can install it again for the next case. |
+ UninstallExtension(id, false, expect_enabled ? Extension::ENABLED |
+ : Extension::DISABLED); |
+ } |
+} |
+ |
+#if defined(ENABLE_SUPERVISED_USERS) |
+ |
+class ExtensionServiceTestSupervised : public ExtensionServiceSyncTest, |
+ public SupervisedUserService::Delegate { |
+ public: |
+ void SetUp() override { |
+ ExtensionServiceSyncTest::SetUp(); |
+ |
+ // This is the update URL specified in the permissions test extension. |
+ // Setting it here is necessary to make the extension considered syncable. |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kAppsGalleryUpdateURL, |
+ "http://localhost/autoupdate/updates.xml"); |
+ } |
+ |
+ void TearDown() override { |
+ supervised_user_service()->SetDelegate(nullptr); |
+ |
+ ExtensionServiceSyncTest::TearDown(); |
+ } |
+ |
+ protected: |
+ void InitServices(bool profile_is_supervised) { |
+ ExtensionServiceInitParams params = CreateDefaultInitParams(); |
+ params.profile_is_supervised = profile_is_supervised; |
+ InitializeExtensionService(params); |
+ |
+ supervised_user_service()->SetDelegate(this); |
+ supervised_user_service()->Init(); |
+ } |
+ |
+ std::string InstallPermissionsTestExtension() { |
+ const std::string version("1"); |
+ |
+ const Extension* extension = |
+ PackAndInstallCRX(dir_path(version), pem_path(), INSTALL_NEW, |
+ Extension::WAS_INSTALLED_BY_CUSTODIAN); |
+ // The extension must now be installed and enabled. |
+ EXPECT_TRUE(extension); |
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
+ EXPECT_EQ(version, extension->VersionString()); |
+ |
+ return extension->id(); |
+ } |
+ |
+ void UpdatePermissionsTestExtension(const std::string& id, |
+ const std::string& version, |
+ UpdateState expected_state) { |
+ PackCRXAndUpdateExtension(id, dir_path(version), pem_path(), |
+ expected_state); |
+ const Extension* extension = registry()->GetInstalledExtension(id); |
+ ASSERT_TRUE(extension); |
+ // The version should have been updated. |
+ EXPECT_EQ(version, extension->VersionString()); |
+ } |
+ |
+ SupervisedUserService* supervised_user_service() { |
+ return SupervisedUserServiceFactory::GetForProfile(profile()); |
+ } |
+ |
+ static std::string UpdateRequestId(const std::string& extension_id, |
+ const std::string& version) { |
+ return SupervisedUserService::GetExtensionUpdateRequestId( |
+ extension_id, base::Version(version)); |
+ } |
+ |
+ private: |
+ // This prevents the legacy supervised user init code from running. |
+ bool SetActive(bool active) override { return true; } |
+ |
+ base::FilePath base_path() const { |
+ return data_dir().AppendASCII("permissions_increase"); |
+ } |
+ base::FilePath dir_path(const std::string& version) const { |
+ return base_path().AppendASCII("v" + version); |
+ } |
+ base::FilePath pem_path() const { |
+ return base_path().AppendASCII("permissions.pem"); |
+ } |
+}; |
+ |
+class MockPermissionRequestCreator : public PermissionRequestCreator { |
+ public: |
+ MockPermissionRequestCreator() {} |
+ ~MockPermissionRequestCreator() override {} |
+ |
+ bool IsEnabled() const override { return true; } |
+ |
+ void CreateURLAccessRequest(const GURL& url_requested, |
+ const SuccessCallback& callback) override { |
+ FAIL(); |
+ } |
+ |
+ MOCK_METHOD2(CreateExtensionUpdateRequest, |
+ void(const std::string& id, |
+ const SupervisedUserService::SuccessCallback& callback)); |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(MockPermissionRequestCreator); |
+}; |
+ |
+TEST_F(ExtensionServiceTestSupervised, InstallOnlyAllowedByCustodian) { |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ base::FilePath path1 = data_dir().AppendASCII("good.crx"); |
+ base::FilePath path2 = data_dir().AppendASCII("good2048.crx"); |
+ const Extension* extensions[] = { |
+ InstallCRX(path1, INSTALL_FAILED), |
+ InstallCRX(path2, INSTALL_NEW, Extension::WAS_INSTALLED_BY_CUSTODIAN) |
+ }; |
+ |
+ // Only the extension with the "installed by custodian" flag should have been |
+ // installed and enabled. |
+ EXPECT_FALSE(extensions[0]); |
+ ASSERT_TRUE(extensions[1]); |
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extensions[1]->id())); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, PreinstalledExtension) { |
+ InitServices(false /* profile_is_supervised */); |
+ |
+ // Install an extension. |
+ base::FilePath path = data_dir().AppendASCII("good.crx"); |
+ const Extension* extension = InstallCRX(path, INSTALL_NEW); |
+ std::string id = extension->id(); |
+ |
+ // Now make the profile supervised. |
+ profile()->AsTestingProfile()->SetSupervisedUserId( |
+ supervised_users::kChildAccountSUID); |
+ |
+ // The extension should not be enabled anymore. |
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(id)); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, UpdateWithoutPermissionIncrease) { |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ base::FilePath base_path = data_dir().AppendASCII("autoupdate"); |
+ base::FilePath pem_path = base_path.AppendASCII("key.pem"); |
+ |
+ const Extension* extension = |
+ PackAndInstallCRX(base_path.AppendASCII("v1"), pem_path, INSTALL_NEW, |
+ Extension::WAS_INSTALLED_BY_CUSTODIAN); |
+ // The extension must now be installed and enabled. |
+ ASSERT_TRUE(extension); |
+ ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id())); |
+ |
+ // Save the id, as the extension object will be destroyed during updating. |
+ std::string id = extension->id(); |
+ |
+ std::string old_version = extension->VersionString(); |
+ |
+ // Update to a new version. |
+ PackCRXAndUpdateExtension(id, base_path.AppendASCII("v2"), pem_path, ENABLED); |
+ |
+ // The extension should still be there and enabled. |
+ extension = registry()->enabled_extensions().GetByID(id); |
+ ASSERT_TRUE(extension); |
+ // The version should have changed. |
+ EXPECT_NE(extension->VersionString(), old_version); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, UpdateWithPermissionIncreaseNoApproval) { |
+ // Explicitly disable the "need custodian approval" field trial. |
+ base::FieldTrialList field_trial_list(new base::MockEntropyProvider()); |
+ base::FieldTrialList::CreateFieldTrial( |
+ "SupervisedUserExtensionPermissionIncrease", ""); |
+ |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ MockPermissionRequestCreator* creator = new MockPermissionRequestCreator; |
+ supervised_user_service()->AddPermissionRequestCreator( |
+ make_scoped_ptr(creator)); |
+ |
+ std::string id = InstallPermissionsTestExtension(); |
+ |
+ // Update to a new version with increased permissions. |
+ // Since we don't require the custodian's approval, no permission request |
+ // should be created. |
+ const std::string version2("2"); |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version2), testing::_)) |
+ .Times(0); |
+ UpdatePermissionsTestExtension(id, version2, DISABLED); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, |
+ UpdateWithPermissionIncreaseApprovalOldVersion) { |
+ // Explicitly enable the "need custodian approval" field trial. |
+ base::FieldTrialList field_trial_list(new base::MockEntropyProvider()); |
+ base::FieldTrialList::CreateFieldTrial( |
+ "SupervisedUserExtensionPermissionIncrease", "NeedCustodianApproval"); |
+ |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ MockPermissionRequestCreator* creator = new MockPermissionRequestCreator; |
+ supervised_user_service()->AddPermissionRequestCreator( |
+ make_scoped_ptr(creator)); |
+ |
+ const std::string version1("1"); |
+ const std::string version2("2"); |
+ |
+ std::string id = InstallPermissionsTestExtension(); |
+ |
+ // Update to a new version with increased permissions. |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version2), testing::_)); |
+ UpdatePermissionsTestExtension(id, version2, DISABLED); |
+ |
+ // Simulate a custodian approval for re-enabling the extension coming in |
+ // through Sync, but set the old version. This can happen when there already |
+ // was a pending request for an earlier version of the extension. |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_disable_reasons(Extension::DISABLE_NONE); |
+ ext_specifics->set_installed_by_custodian(true); |
+ ext_specifics->set_version(version1); |
+ |
+ // Attempting to re-enable an old version should result in a permission |
+ // request for the current version. |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version2), testing::_)); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(id, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList change_list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, change_list); |
+ // The re-enable should be ignored, since the version doesn't match. |
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(id)); |
+ EXPECT_FALSE(extension_sync_service()->HasPendingReenable( |
+ id, base::Version(version1))); |
+ EXPECT_FALSE(extension_sync_service()->HasPendingReenable( |
+ id, base::Version(version2))); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, |
+ UpdateWithPermissionIncreaseApprovalMatchingVersion) { |
+ // Explicitly enable the "need custodian approval" field trial. |
+ base::FieldTrialList field_trial_list(new base::MockEntropyProvider()); |
+ base::FieldTrialList::CreateFieldTrial( |
+ "SupervisedUserExtensionPermissionIncrease", "NeedCustodianApproval"); |
+ |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ MockPermissionRequestCreator* creator = new MockPermissionRequestCreator; |
+ supervised_user_service()->AddPermissionRequestCreator( |
+ make_scoped_ptr(creator)); |
+ |
+ std::string id = InstallPermissionsTestExtension(); |
+ |
+ // Update to a new version with increased permissions. |
+ const std::string version2("2"); |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version2), testing::_)); |
+ UpdatePermissionsTestExtension(id, version2, DISABLED); |
+ |
+ // Simulate a custodian approval for re-enabling the extension coming in |
+ // through Sync. |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_disable_reasons(Extension::DISABLE_NONE); |
+ ext_specifics->set_installed_by_custodian(true); |
+ ext_specifics->set_version(version2); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(id, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList change_list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, change_list); |
+ // The extension should have gotten re-enabled. |
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(id)); |
+} |
+ |
+TEST_F(ExtensionServiceTestSupervised, |
+ UpdateWithPermissionIncreaseApprovalNewVersion) { |
+ // Explicitly enable the "need custodian approval" field trial. |
+ base::FieldTrialList field_trial_list(new base::MockEntropyProvider()); |
+ base::FieldTrialList::CreateFieldTrial( |
+ "SupervisedUserExtensionPermissionIncrease", "NeedCustodianApproval"); |
+ |
+ InitServices(true /* profile_is_supervised */); |
+ |
+ MockPermissionRequestCreator* creator = new MockPermissionRequestCreator; |
+ supervised_user_service()->AddPermissionRequestCreator( |
+ make_scoped_ptr(creator)); |
+ |
+ std::string id = InstallPermissionsTestExtension(); |
+ |
+ // Update to a new version with increased permissions. |
+ const std::string version2("2"); |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version2), testing::_)); |
+ UpdatePermissionsTestExtension(id, version2, DISABLED); |
+ |
+ // Simulate a custodian approval for re-enabling the extension coming in |
+ // through Sync. Set a newer version than we have installed. |
+ const std::string version3("3"); |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_enabled(true); |
+ ext_specifics->set_disable_reasons(Extension::DISABLE_NONE); |
+ ext_specifics->set_installed_by_custodian(true); |
+ ext_specifics->set_version(version3); |
+ |
+ // This should *not* result in a new permission request. |
+ EXPECT_CALL(*creator, CreateExtensionUpdateRequest( |
+ UpdateRequestId(id, version3), testing::_)) |
+ .Times(0); |
+ |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(id, "Name", specifics); |
+ syncer::SyncChange sync_change(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, |
+ sync_data); |
+ syncer::SyncChangeList change_list(1, sync_change); |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, change_list); |
+ // The re-enable should be delayed until the extension is updated to the |
+ // matching version. |
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(id)); |
+ EXPECT_TRUE(extension_sync_service()->HasPendingReenable( |
+ id, base::Version(version3))); |
+ |
+ // Update to the matching version. Now the extension should get enabled. |
+ UpdatePermissionsTestExtension(id, version3, ENABLED); |
+} |
+ |
+TEST_F(ExtensionServiceSyncTest, SyncUninstallByCustodianSkipsPolicy) { |
+ InitializeEmptyExtensionService(); |
+ extension_sync_service()->MergeDataAndStartSyncing( |
+ syncer::EXTENSIONS, |
+ syncer::SyncDataList(), |
+ scoped_ptr<syncer::SyncChangeProcessor>( |
+ new syncer::FakeSyncChangeProcessor), |
+ scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); |
+ |
+ // Install two extensions. |
+ base::FilePath path1 = data_dir().AppendASCII("good.crx"); |
+ base::FilePath path2 = data_dir().AppendASCII("good2048.crx"); |
+ const Extension* extensions[] = { |
+ InstallCRX(path1, INSTALL_NEW), |
+ InstallCRX(path2, INSTALL_NEW, Extension::WAS_INSTALLED_BY_CUSTODIAN) |
+ }; |
+ |
+ // Add a policy provider that will disallow any changes. |
+ extensions::TestManagementPolicyProvider provider( |
+ extensions::TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS); |
+ ExtensionSystem::Get( |
+ browser_context())->management_policy()->RegisterProvider(&provider); |
+ |
+ // Create a sync deletion for each extension. |
+ syncer::SyncChangeList change_list; |
+ for (size_t i = 0; i < arraysize(extensions); i++) { |
+ const std::string& id = extensions[i]->id(); |
+ sync_pb::EntitySpecifics specifics; |
+ sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension(); |
+ ext_specifics->set_id(id); |
+ ext_specifics->set_version("1.0"); |
+ ext_specifics->set_installed_by_custodian( |
+ extensions[i]->was_installed_by_custodian()); |
+ syncer::SyncData sync_data = |
+ syncer::SyncData::CreateLocalData(id, "Name", specifics); |
+ change_list.push_back(syncer::SyncChange(FROM_HERE, |
+ syncer::SyncChange::ACTION_DELETE, |
+ sync_data)); |
+ } |
+ |
+ // Save the extension ids, as uninstalling destroys the Extension instance. |
+ std::string extension_ids[] = { |
+ extensions[0]->id(), |
+ extensions[1]->id() |
+ }; |
+ |
+ // Now apply the uninstallations. |
+ extension_sync_service()->ProcessSyncChanges(FROM_HERE, change_list); |
+ |
+ // Uninstalling the extension without installed_by_custodian should have been |
+ // blocked by policy, so it should still be there. |
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_ids[0])); |
+ |
+ // But installed_by_custodian should result in bypassing the policy check. |
+ EXPECT_FALSE( |
+ registry()->GenerateInstalledExtensionsSet()->Contains(extension_ids[1])); |
+} |
+ |
+#endif // defined(ENABLE_SUPERVISED_USERS) |