| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/apps/ephemeral_app_browsertest.h" |
| |
| #include <vector> |
| |
| #include "apps/saved_files_service.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/scoped_observer.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/apps/app_browsertest_util.h" |
| #include "chrome/browser/extensions/api/file_system/file_system_api.h" |
| #include "chrome/browser/extensions/app_sync_data.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_sync_service.h" |
| #include "chrome/browser/extensions/extension_test_message_listener.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/notifications/desktop_notification_service.h" |
| #include "chrome/browser/notifications/desktop_notification_service_factory.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/api/alarms.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/app_sorting.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_registry_observer.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/extension_util.h" |
| #include "extensions/browser/process_manager.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/switches.h" |
| #include "sync/api/fake_sync_change_processor.h" |
| #include "sync/api/sync_change_processor_wrapper_for_test.h" |
| #include "sync/api/sync_error_factory_mock.h" |
| #include "ui/message_center/message_center.h" |
| #include "ui/message_center/notifier_settings.h" |
| |
| using extensions::AppSyncData; |
| using extensions::Event; |
| using extensions::EventRouter; |
| using extensions::Extension; |
| using extensions::ExtensionPrefs; |
| using extensions::ExtensionRegistry; |
| using extensions::ExtensionRegistryObserver; |
| using extensions::ExtensionSystem; |
| using extensions::Manifest; |
| |
| namespace { |
| |
| namespace alarms = extensions::api::alarms; |
| |
| const char kNotificationsTestApp[] = "ephemeral_apps/notification_settings"; |
| const char kFileSystemTestApp[] = "ephemeral_apps/filesystem_retain_entries"; |
| |
| typedef std::vector<message_center::Notifier*> NotifierList; |
| |
| bool IsNotifierInList(const message_center::NotifierId& notifier_id, |
| const NotifierList& notifiers) { |
| for (NotifierList::const_iterator it = notifiers.begin(); |
| it != notifiers.end(); ++it) { |
| const message_center::Notifier* notifier = *it; |
| if (notifier->notifier_id == notifier_id) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Saves some parameters from the extension installed notification in order |
| // to verify them in tests. |
| class InstallObserver : public ExtensionRegistryObserver { |
| public: |
| struct InstallParameters { |
| std::string id; |
| bool is_update; |
| bool from_ephemeral; |
| |
| InstallParameters( |
| const std::string& id, |
| bool is_update, |
| bool from_ephemeral) |
| : id(id), is_update(is_update), from_ephemeral(from_ephemeral) {} |
| }; |
| |
| explicit InstallObserver(Profile* profile) : registry_observer_(this) { |
| registry_observer_.Add(ExtensionRegistry::Get(profile)); |
| } |
| |
| virtual ~InstallObserver() {} |
| |
| const InstallParameters& Last() { |
| CHECK(!install_params_.empty()); |
| return install_params_.back(); |
| } |
| |
| private: |
| virtual void OnExtensionWillBeInstalled( |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| bool is_update, |
| bool from_ephemeral, |
| const std::string& old_name) OVERRIDE { |
| install_params_.push_back( |
| InstallParameters(extension->id(), is_update, from_ephemeral)); |
| } |
| |
| std::vector<InstallParameters> install_params_; |
| ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> |
| registry_observer_; |
| }; |
| |
| } // namespace |
| |
| |
| // EphemeralAppTestBase: |
| |
| const char EphemeralAppTestBase::kMessagingReceiverApp[] = |
| "ephemeral_apps/messaging_receiver"; |
| const char EphemeralAppTestBase::kMessagingReceiverAppV2[] = |
| "ephemeral_apps/messaging_receiver2"; |
| const char EphemeralAppTestBase::kDispatchEventTestApp[] = |
| "ephemeral_apps/dispatch_event"; |
| |
| EphemeralAppTestBase::EphemeralAppTestBase() {} |
| |
| EphemeralAppTestBase::~EphemeralAppTestBase() {} |
| |
| void EphemeralAppTestBase::SetUpCommandLine(base::CommandLine* command_line) { |
| // Skip PlatformAppBrowserTest, which sets different values for the switches |
| // below. |
| ExtensionBrowserTest::SetUpCommandLine(command_line); |
| |
| // Make event pages get suspended immediately. |
| command_line->AppendSwitchASCII( |
| extensions::switches::kEventPageIdleTime, "10"); |
| command_line->AppendSwitchASCII( |
| extensions::switches::kEventPageSuspendingTime, "10"); |
| |
| // Enable ephemeral apps flag. |
| command_line->AppendSwitch(switches::kEnableEphemeralApps); |
| } |
| |
| base::FilePath EphemeralAppTestBase::GetTestPath(const char* test_path) { |
| return test_data_dir_.AppendASCII("platform_apps").AppendASCII(test_path); |
| } |
| |
| const Extension* EphemeralAppTestBase::InstallEphemeralApp( |
| const char* test_path, Manifest::Location manifest_location) { |
| const Extension* extension = InstallEphemeralAppWithSourceAndFlags( |
| GetTestPath(test_path), 1, manifest_location, Extension::NO_FLAGS); |
| EXPECT_TRUE(extension); |
| if (extension) |
| EXPECT_TRUE(extensions::util::IsEphemeralApp(extension->id(), profile())); |
| return extension; |
| } |
| |
| const Extension* EphemeralAppTestBase::InstallEphemeralApp( |
| const char* test_path) { |
| return InstallEphemeralApp(test_path, Manifest::INTERNAL); |
| } |
| |
| const Extension* EphemeralAppTestBase::InstallAndLaunchEphemeralApp( |
| const char* test_path) { |
| ExtensionTestMessageListener launched_listener("launched", false); |
| const Extension* extension = InstallEphemeralApp(test_path); |
| EXPECT_TRUE(extension); |
| if (!extension) |
| return NULL; |
| |
| LaunchPlatformApp(extension); |
| bool wait_result = launched_listener.WaitUntilSatisfied(); |
| EXPECT_TRUE(wait_result); |
| if (!wait_result) |
| return NULL; |
| |
| return extension; |
| } |
| |
| const Extension* EphemeralAppTestBase::UpdateEphemeralApp( |
| const std::string& app_id, |
| const base::FilePath& test_dir, |
| const base::FilePath& pem_path) { |
| // Pack a new version of the app. |
| base::ScopedTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); |
| if (!base::DeleteFile(crx_path, false)) { |
| ADD_FAILURE() << "Failed to delete existing crx: " << crx_path.value(); |
| return NULL; |
| } |
| |
| base::FilePath app_v2_path = PackExtensionWithOptions( |
| test_dir, crx_path, pem_path, base::FilePath()); |
| EXPECT_FALSE(app_v2_path.empty()); |
| |
| // Update the ephemeral app and wait for the update to finish. |
| extensions::CrxInstaller* crx_installer = NULL; |
| content::WindowedNotificationObserver windowed_observer( |
| chrome::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::Source<extensions::CrxInstaller>(crx_installer)); |
| ExtensionService* service = |
| ExtensionSystem::Get(profile())->extension_service(); |
| EXPECT_TRUE(service->UpdateExtension(app_id, app_v2_path, true, |
| &crx_installer)); |
| windowed_observer.Wait(); |
| |
| return service->GetExtensionById(app_id, false); |
| } |
| |
| void EphemeralAppTestBase::PromoteEphemeralApp( |
| const extensions::Extension* app) { |
| ExtensionService* extension_service = |
| ExtensionSystem::Get(profile())->extension_service(); |
| ASSERT_TRUE(extension_service); |
| extension_service->PromoteEphemeralApp(app, false); |
| } |
| |
| void EphemeralAppTestBase::CloseApp(const std::string& app_id) { |
| content::WindowedNotificationObserver event_page_destroyed_signal( |
| chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, |
| content::Source<Profile>(profile())); |
| |
| EXPECT_EQ(1U, GetAppWindowCountForApp(app_id)); |
| apps::AppWindow* app_window = GetFirstAppWindowForApp(app_id); |
| ASSERT_TRUE(app_window); |
| CloseAppWindow(app_window); |
| |
| event_page_destroyed_signal.Wait(); |
| } |
| |
| void EphemeralAppTestBase::EvictApp(const std::string& app_id) { |
| // Uninstall the app, which is what happens when ephemeral apps get evicted |
| // from the cache. |
| content::WindowedNotificationObserver uninstalled_signal( |
| chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, |
| content::Source<Profile>(profile())); |
| |
| ExtensionService* service = |
| ExtensionSystem::Get(profile())->extension_service(); |
| ASSERT_TRUE(service); |
| service->UninstallExtension( |
| app_id, |
| ExtensionService::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, |
| NULL); |
| |
| uninstalled_signal.Wait(); |
| } |
| |
| // EphemeralAppBrowserTest: |
| |
| class EphemeralAppBrowserTest : public EphemeralAppTestBase { |
| protected: |
| bool LaunchAppAndRunTest(const Extension* app, const char* test_name) { |
| ExtensionTestMessageListener launched_listener("launched", true); |
| LaunchPlatformApp(app); |
| if (!launched_listener.WaitUntilSatisfied()) { |
| message_ = "Failed to receive launched message from test"; |
| return false; |
| } |
| |
| ResultCatcher catcher; |
| launched_listener.Reply(test_name); |
| |
| bool result = catcher.GetNextResult(); |
| message_ = catcher.message(); |
| |
| CloseApp(app->id()); |
| return result; |
| } |
| |
| void VerifyAppNotLoaded(const std::string& app_id) { |
| EXPECT_FALSE(ExtensionSystem::Get(profile())-> |
| process_manager()->GetBackgroundHostForExtension(app_id)); |
| } |
| |
| void DispatchAlarmEvent(EventRouter* event_router, |
| const std::string& app_id) { |
| alarms::Alarm dummy_alarm; |
| dummy_alarm.name = "test_alarm"; |
| |
| scoped_ptr<base::ListValue> args(new base::ListValue()); |
| args->Append(dummy_alarm.ToValue().release()); |
| scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName, |
| args.Pass())); |
| |
| event_router->DispatchEventToExtension(app_id, event.Pass()); |
| } |
| |
| const Extension* ReplaceEphemeralApp(const std::string& app_id, |
| const char* test_path) { |
| return UpdateExtensionWaitForIdle(app_id, GetTestPath(test_path), 0); |
| } |
| |
| void VerifyPromotedApp(const std::string& app_id, |
| ExtensionRegistry::IncludeFlag expected_set) { |
| const Extension* app = ExtensionRegistry::Get(profile())->GetExtensionById( |
| app_id, expected_set); |
| ASSERT_TRUE(app); |
| |
| // The app should not be ephemeral. |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| ASSERT_TRUE(prefs); |
| EXPECT_FALSE(prefs->IsEphemeralApp(app_id)); |
| |
| // Check sort ordinals. |
| extensions::AppSorting* app_sorting = prefs->app_sorting(); |
| EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).IsValid()); |
| EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).IsValid()); |
| } |
| |
| void InitSyncService() { |
| ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile()); |
| sync_service->MergeDataAndStartSyncing( |
| syncer::APPS, |
| syncer::SyncDataList(), |
| scoped_ptr<syncer::SyncChangeProcessor>( |
| new syncer::SyncChangeProcessorWrapperForTest( |
| &mock_sync_processor_)), |
| scoped_ptr<syncer::SyncErrorFactory>( |
| new syncer::SyncErrorFactoryMock())); |
| } |
| |
| scoped_ptr<AppSyncData> GetFirstSyncChangeForApp(const std::string& id) { |
| scoped_ptr<AppSyncData> sync_data; |
| for (syncer::SyncChangeList::iterator it = |
| mock_sync_processor_.changes().begin(); |
| it != mock_sync_processor_.changes().end(); ++it) { |
| sync_data.reset(new AppSyncData(*it)); |
| if (sync_data->id() == id) |
| return sync_data.Pass(); |
| } |
| |
| return scoped_ptr<AppSyncData>(); |
| } |
| |
| void VerifySyncChange(const AppSyncData* sync_change, bool expect_enabled) { |
| ASSERT_TRUE(sync_change); |
| EXPECT_TRUE(sync_change->page_ordinal().IsValid()); |
| EXPECT_TRUE(sync_change->app_launch_ordinal().IsValid()); |
| EXPECT_FALSE(sync_change->uninstalled()); |
| EXPECT_EQ(expect_enabled, sync_change->extension_sync_data().enabled()); |
| } |
| |
| syncer::FakeSyncChangeProcessor mock_sync_processor_; |
| }; |
| |
| // Verify that ephemeral apps can be launched and receive system events when |
| // they are running. Once they are inactive they should not receive system |
| // events. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, EventDispatchWhenLaunched) { |
| const Extension* extension = |
| InstallAndLaunchEphemeralApp(kDispatchEventTestApp); |
| ASSERT_TRUE(extension); |
| |
| // Send a fake alarm event to the app and verify that a response is |
| // received. |
| EventRouter* event_router = EventRouter::Get(profile()); |
| ASSERT_TRUE(event_router); |
| |
| ExtensionTestMessageListener alarm_received_listener("alarm_received", false); |
| DispatchAlarmEvent(event_router, extension->id()); |
| ASSERT_TRUE(alarm_received_listener.WaitUntilSatisfied()); |
| |
| CloseApp(extension->id()); |
| |
| // The app needs to be launched once in order to have the onAlarm() event |
| // registered. |
| ASSERT_TRUE(event_router->ExtensionHasEventListener( |
| extension->id(), alarms::OnAlarm::kEventName)); |
| |
| // Dispatch the alarm event again and verify that the event page did not get |
| // loaded for the app. |
| DispatchAlarmEvent(event_router, extension->id()); |
| VerifyAppNotLoaded(extension->id()); |
| } |
| |
| // Verify that ephemeral apps will receive messages while they are running. |
| // Flaky test: crbug.com/394426 |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| DISABLED_ReceiveMessagesWhenLaunched) { |
| const Extension* receiver = |
| InstallAndLaunchEphemeralApp(kMessagingReceiverApp); |
| ASSERT_TRUE(receiver); |
| |
| // Verify that messages are received while the app is running. |
| ExtensionApiTest::ResultCatcher result_catcher; |
| LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_success", |
| "Launched"); |
| EXPECT_TRUE(result_catcher.GetNextResult()); |
| |
| CloseApp(receiver->id()); |
| |
| // Verify that messages are not received while the app is inactive. |
| LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_fail", "Launched"); |
| EXPECT_TRUE(result_catcher.GetNextResult()); |
| } |
| |
| // Verify that an updated ephemeral app will still have its ephemeral flag |
| // enabled. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, UpdateEphemeralApp) { |
| const Extension* app_v1 = InstallEphemeralApp(kMessagingReceiverApp); |
| ASSERT_TRUE(app_v1); |
| std::string app_id = app_v1->id(); |
| base::Version app_original_version = *app_v1->version(); |
| app_v1 = NULL; // The extension object will be destroyed during update. |
| |
| // Update to version 2 of the app. |
| InstallObserver installed_observer(profile()); |
| const Extension* app_v2 = UpdateEphemeralApp( |
| app_id, GetTestPath(kMessagingReceiverAppV2), |
| GetTestPath(kMessagingReceiverApp).ReplaceExtension( |
| FILE_PATH_LITERAL(".pem"))); |
| |
| // Check the notification parameters. |
| const InstallObserver::InstallParameters& params = installed_observer.Last(); |
| EXPECT_EQ(app_id, params.id); |
| EXPECT_TRUE(params.is_update); |
| EXPECT_FALSE(params.from_ephemeral); |
| |
| // The ephemeral flag should still be enabled. |
| ASSERT_TRUE(app_v2); |
| EXPECT_TRUE(app_v2->version()->CompareTo(app_original_version) > 0); |
| EXPECT_TRUE(extensions::util::IsEphemeralApp(app_v2->id(), profile())); |
| } |
| |
| // Verify that if notifications have been disabled for an ephemeral app, it will |
| // remain disabled even after being evicted from the cache. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, StickyNotificationSettings) { |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| |
| // Disable notifications for this app. |
| DesktopNotificationService* notification_service = |
| DesktopNotificationServiceFactory::GetForProfile(profile()); |
| ASSERT_TRUE(notification_service); |
| |
| message_center::NotifierId notifier_id( |
| message_center::NotifierId::APPLICATION, app->id()); |
| EXPECT_TRUE(notification_service->IsNotifierEnabled(notifier_id)); |
| notification_service->SetNotifierEnabled(notifier_id, false); |
| EXPECT_FALSE(notification_service->IsNotifierEnabled(notifier_id)); |
| |
| // Remove the app. |
| EvictApp(app->id()); |
| |
| // Reinstall the ephemeral app and verify that notifications remain disabled. |
| app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| message_center::NotifierId reinstalled_notifier_id( |
| message_center::NotifierId::APPLICATION, app->id()); |
| EXPECT_FALSE(notification_service->IsNotifierEnabled( |
| reinstalled_notifier_id)); |
| } |
| |
| // Verify that only running ephemeral apps will appear in the Notification |
| // Settings UI. Inactive, cached ephemeral apps should not appear. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| IncludeRunningEphemeralAppsInNotifiers) { |
| message_center::NotifierSettingsProvider* settings_provider = |
| message_center::MessageCenter::Get()->GetNotifierSettingsProvider(); |
| // TODO(tmdiep): Remove once notifications settings are supported across |
| // all platforms. This test will fail for Linux GTK. |
| if (!settings_provider) |
| return; |
| |
| const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| message_center::NotifierId notifier_id( |
| message_center::NotifierId::APPLICATION, app->id()); |
| |
| // Since the ephemeral app is running, it should be included in the list |
| // of notifiers to show in the UI. |
| NotifierList notifiers; |
| STLElementDeleter<NotifierList> notifier_deleter(¬ifiers); |
| |
| settings_provider->GetNotifierList(¬ifiers); |
| EXPECT_TRUE(IsNotifierInList(notifier_id, notifiers)); |
| STLDeleteElements(¬ifiers); |
| |
| // Close the ephemeral app. |
| CloseApp(app->id()); |
| |
| // Inactive ephemeral apps should not be included in the list of notifiers to |
| // show in the UI. |
| settings_provider->GetNotifierList(¬ifiers); |
| EXPECT_FALSE(IsNotifierInList(notifier_id, notifiers)); |
| } |
| |
| // Verify that ephemeral apps will have no ability to retain file entries after |
| // close. Normal retainEntry behavior for installed apps is tested in |
| // FileSystemApiTest. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| DisableRetainFileSystemEntries) { |
| // Create a dummy file that we can just return to the test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath temp_file; |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file)); |
| |
| using extensions::FileSystemChooseEntryFunction; |
| FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest( |
| &temp_file); |
| // The temporary file needs to be registered for the tests to pass on |
| // ChromeOS. |
| FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest( |
| "temp", temp_dir.path()); |
| |
| // The first test opens the file and writes the file handle to local storage. |
| const Extension* app = InstallEphemeralApp(kFileSystemTestApp, |
| Manifest::UNPACKED); |
| ASSERT_TRUE(LaunchAppAndRunTest(app, "OpenAndRetainFile")) << message_; |
| |
| // Verify that after the app has been closed, all retained entries are |
| // flushed. |
| std::vector<apps::SavedFileEntry> file_entries = |
| apps::SavedFilesService::Get(profile()) |
| ->GetAllFileEntries(app->id()); |
| EXPECT_TRUE(file_entries.empty()); |
| |
| // The second test verifies that the file cannot be reopened. |
| ASSERT_TRUE(LaunchAppAndRunTest(app, "RestoreRetainedFile")) << message_; |
| } |
| |
| // Checks the process of installing and then promoting an ephemeral app. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralApp) { |
| InitSyncService(); |
| |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| |
| // Ephemeral apps should not be synced. |
| scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id()); |
| EXPECT_FALSE(sync_change.get()); |
| |
| // Promote the app to a regular installed app. |
| InstallObserver installed_observer(profile()); |
| PromoteEphemeralApp(app); |
| VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED); |
| |
| // Check the notification parameters. |
| const InstallObserver::InstallParameters& params = installed_observer.Last(); |
| EXPECT_EQ(app->id(), params.id); |
| EXPECT_TRUE(params.is_update); |
| EXPECT_TRUE(params.from_ephemeral); |
| |
| // The installation should now be synced. |
| sync_change = GetFirstSyncChangeForApp(app->id()); |
| VerifySyncChange(sync_change.get(), true); |
| } |
| |
| // Verifies that promoting an ephemeral app will enable it. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppAndEnable) { |
| InitSyncService(); |
| |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| |
| // Disable the ephemeral app due to a permissions increase. This also involves |
| // setting the DidExtensionEscalatePermissions flag. |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| prefs->SetDidExtensionEscalatePermissions(app, true); |
| ExtensionService* service = |
| ExtensionSystem::Get(profile())->extension_service(); |
| service->DisableExtension(app->id(), Extension::DISABLE_PERMISSIONS_INCREASE); |
| ASSERT_TRUE(ExtensionRegistry::Get(profile())-> |
| GetExtensionById(app->id(), ExtensionRegistry::DISABLED)); |
| |
| // Promote to a regular installed app. It should be enabled. |
| PromoteEphemeralApp(app); |
| VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED); |
| EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(app->id())); |
| |
| scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id()); |
| VerifySyncChange(sync_change.get(), true); |
| } |
| |
| // Verifies that promoting an ephemeral app that has unsupported requirements |
| // will not enable it. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| PromoteUnsupportedEphemeralApp) { |
| InitSyncService(); |
| |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| |
| // Disable the ephemeral app. |
| ExtensionService* service = |
| ExtensionSystem::Get(profile())->extension_service(); |
| service->DisableExtension( |
| app->id(), Extension::DISABLE_UNSUPPORTED_REQUIREMENT); |
| ASSERT_TRUE(ExtensionRegistry::Get(profile())-> |
| GetExtensionById(app->id(), ExtensionRegistry::DISABLED)); |
| |
| // Promote to a regular installed app. It should remain disabled. |
| PromoteEphemeralApp(app); |
| VerifyPromotedApp(app->id(), ExtensionRegistry::DISABLED); |
| |
| scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id()); |
| VerifySyncChange(sync_change.get(), false); |
| } |
| |
| // Checks the process of promoting an ephemeral app from sync. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppFromSync) { |
| InitSyncService(); |
| |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| std::string app_id = app->id(); |
| |
| // Simulate an install from sync. |
| const syncer::StringOrdinal kAppLaunchOrdinal("x"); |
| const syncer::StringOrdinal kPageOrdinal("y"); |
| AppSyncData app_sync_data( |
| *app, |
| true /* enabled */, |
| false /* incognito enabled */, |
| false /* remote install */, |
| kAppLaunchOrdinal, |
| kPageOrdinal, |
| extensions::LAUNCH_TYPE_REGULAR); |
| |
| ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile()); |
| sync_service->ProcessAppSyncData(app_sync_data); |
| |
| // Verify the installation. |
| VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED); |
| |
| // The sort ordinals from sync should not be overridden. |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| extensions::AppSorting* app_sorting = prefs->app_sorting(); |
| EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).Equals( |
| kAppLaunchOrdinal)); |
| EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).Equals(kPageOrdinal)); |
| } |
| |
| // In most cases, ExtensionService::PromoteEphemeralApp() will be called to |
| // permanently install an ephemeral app. However, there may be cases where an |
| // install occurs through the usual route of installing from the Web Store (due |
| // to race conditions). Ensure that the app is still installed correctly. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| ReplaceEphemeralAppWithInstalledApp) { |
| const Extension* app = InstallEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| std::string app_id = app->id(); |
| app = NULL; |
| |
| InstallObserver installed_observer(profile()); |
| ReplaceEphemeralApp(app_id, kNotificationsTestApp); |
| VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED); |
| |
| // Check the notification parameters. |
| const InstallObserver::InstallParameters& params = installed_observer.Last(); |
| EXPECT_EQ(app_id, params.id); |
| EXPECT_TRUE(params.is_update); |
| EXPECT_TRUE(params.from_ephemeral); |
| } |
| |
| // This is similar to ReplaceEphemeralAppWithInstalledApp, but installs will |
| // be delayed until the app is idle. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| ReplaceEphemeralAppWithDelayedInstalledApp) { |
| const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp); |
| ASSERT_TRUE(app); |
| std::string app_id = app->id(); |
| app = NULL; |
| |
| // Initiate install. |
| ReplaceEphemeralApp(app_id, kNotificationsTestApp); |
| |
| // The delayed installation will occur when the ephemeral app is closed. |
| content::WindowedNotificationObserver installed_signal( |
| chrome::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, |
| content::Source<Profile>(profile())); |
| InstallObserver installed_observer(profile()); |
| CloseApp(app_id); |
| installed_signal.Wait(); |
| VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED); |
| |
| // Check the notification parameters. |
| const InstallObserver::InstallParameters& params = installed_observer.Last(); |
| EXPECT_EQ(app_id, params.id); |
| EXPECT_TRUE(params.is_update); |
| EXPECT_TRUE(params.from_ephemeral); |
| } |
| |
| // Ephemerality was previously encoded by the Extension::IS_EPHEMERAL creation |
| // flag. This was changed to an "ephemeral_app" property. Check that the prefs |
| // are handled correctly. |
| IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, |
| ExtensionPrefBackcompatibility) { |
| // Ensure that apps with the old prefs are recognized as ephemeral. |
| const Extension* app = |
| InstallExtensionWithSourceAndFlags(GetTestPath(kNotificationsTestApp), |
| 1, |
| Manifest::INTERNAL, |
| Extension::IS_EPHEMERAL); |
| ASSERT_TRUE(app); |
| EXPECT_TRUE(extensions::util::IsEphemeralApp(app->id(), profile())); |
| |
| // Ensure that when the app is promoted to an installed app, the bit in the |
| // creation flags is cleared. |
| PromoteEphemeralApp(app); |
| EXPECT_FALSE(extensions::util::IsEphemeralApp(app->id(), profile())); |
| |
| int creation_flags = |
| ExtensionPrefs::Get(profile())->GetCreationFlags(app->id()); |
| EXPECT_EQ(0, creation_flags & Extension::IS_EPHEMERAL); |
| } |