Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(980)

Unified Diff: chrome/browser/extensions/extension_service_sync_unittest.cc

Issue 1411773002: Move Sync-specific tests from ExtensionServiceTest into new file (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@su_ext_reenable
Patch Set: move ExternalInstallPrompt override Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chrome/browser/extensions/extension_service_test_base.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)
« no previous file with comments | « no previous file | chrome/browser/extensions/extension_service_test_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698