1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/apps/ephemeral_app_browsertest.h"
6
7 #include <vector>
8
9 #include "apps/saved_files_service.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/scoped_observer.h"
12 #include "base/stl_util.h"
13 #include "chrome/browser/apps/app_browsertest_util.h"
14 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
15 #include "chrome/browser/extensions/app_sync_data.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_sync_service.h"
18 #include "chrome/browser/extensions/extension_test_message_listener.h"
19 #include "chrome/browser/extensions/extension_util.h"
20 #include "chrome/browser/notifications/desktop_notification_service.h"
21 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/extensions/api/alarms.h"
24 #include "content/public/test/browser_test.h"
25 #include "content/public/test/test_utils.h"
26 #include "extensions/browser/app_sorting.h"
27 #include "extensions/browser/event_router.h"
28 #include "extensions/browser/extension_prefs.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/extension_registry_observer.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/extension_util.h"
33 #include "extensions/browser/process_manager.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/switches.h"
36 #include "sync/api/fake_sync_change_processor.h"
37 #include "sync/api/sync_change_processor_wrapper_for_test.h"
38 #include "sync/api/sync_error_factory_mock.h"
39 #include "ui/message_center/message_center.h"
40 #include "ui/message_center/notifier_settings.h"
41
42 using extensions::AppSyncData;
43 using extensions::Event;
44 using extensions::EventRouter;
45 using extensions::Extension;
46 using extensions::ExtensionPrefs;
47 using extensions::ExtensionRegistry;
48 using extensions::ExtensionRegistryObserver;
49 using extensions::ExtensionSystem;
50 using extensions::Manifest;
51
52 namespace {
53
54 namespace alarms = extensions::api::alarms;
55
56 const char kDispatchEventTestApp[] = "ephemeral_apps/dispatch_event";
57 const char kNotificationsTestApp[] = "ephemeral_apps/notification_settings";
58 const char kFileSystemTestApp[] = "ephemeral_apps/filesystem_retain_entries";
59
60 typedef std::vector<message_center::Notifier*> NotifierList;
61
IsNotifierInList(const message_center::NotifierId & notifier_id,const NotifierList & notifiers)62 bool IsNotifierInList(const message_center::NotifierId& notifier_id,
63 const NotifierList& notifiers) {
64 for (NotifierList::const_iterator it = notifiers.begin();
65 it != notifiers.end(); ++it) {
66 const message_center::Notifier* notifier = *it;
67 if (notifier->notifier_id == notifier_id)
68 return true;
69 }
70
71 return false;
72 }
73
74 // Saves some parameters from the extension installed notification in order
75 // to verify them in tests.
76 class InstallObserver : public ExtensionRegistryObserver {
77 public:
78 struct InstallParameters {
79 std::string id;
80 bool is_update;
81 bool from_ephemeral;
82
InstallParameters__anon52cacdbc0111::InstallObserver::InstallParameters83 InstallParameters(
84 const std::string& id,
85 bool is_update,
86 bool from_ephemeral)
87 : id(id), is_update(is_update), from_ephemeral(from_ephemeral) {}
88 };
89
InstallObserver(Profile * profile)90 explicit InstallObserver(Profile* profile) : registry_observer_(this) {
91 registry_observer_.Add(ExtensionRegistry::Get(profile));
92 }
93
~InstallObserver()94 virtual ~InstallObserver() {}
95
Last()96 const InstallParameters& Last() {
97 CHECK(!install_params_.empty());
98 return install_params_.back();
99 }
100
101 private:
OnExtensionWillBeInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update,bool from_ephemeral,const std::string & old_name)102 virtual void OnExtensionWillBeInstalled(
103 content::BrowserContext* browser_context,
104 const Extension* extension,
105 bool is_update,
106 bool from_ephemeral,
107 const std::string& old_name) OVERRIDE {
108 install_params_.push_back(
109 InstallParameters(extension->id(), is_update, from_ephemeral));
110 }
111
112 std::vector<InstallParameters> install_params_;
113 ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
114 registry_observer_;
115 };
116
117 } // namespace
118
119
120 // EphemeralAppTestBase:
121
122 const char EphemeralAppTestBase::kMessagingReceiverApp[] =
123 "ephemeral_apps/messaging_receiver";
124 const char EphemeralAppTestBase::kMessagingReceiverAppV2[] =
125 "ephemeral_apps/messaging_receiver2";
126
EphemeralAppTestBase()127 EphemeralAppTestBase::EphemeralAppTestBase() {}
128
~EphemeralAppTestBase()129 EphemeralAppTestBase::~EphemeralAppTestBase() {}
130
SetUpCommandLine(base::CommandLine * command_line)131 void EphemeralAppTestBase::SetUpCommandLine(base::CommandLine* command_line) {
132 // Skip PlatformAppBrowserTest, which sets different values for the switches
133 // below.
134 ExtensionBrowserTest::SetUpCommandLine(command_line);
135
136 // Make event pages get suspended immediately.
137 command_line->AppendSwitchASCII(
138 extensions::switches::kEventPageIdleTime, "10");
139 command_line->AppendSwitchASCII(
140 extensions::switches::kEventPageSuspendingTime, "10");
141
142 // Enable ephemeral apps flag.
143 command_line->AppendSwitch(switches::kEnableEphemeralApps);
144 }
145
GetTestPath(const char * test_path)146 base::FilePath EphemeralAppTestBase::GetTestPath(const char* test_path) {
147 return test_data_dir_.AppendASCII("platform_apps").AppendASCII(test_path);
148 }
149
InstallEphemeralApp(const char * test_path,Manifest::Location manifest_location)150 const Extension* EphemeralAppTestBase::InstallEphemeralApp(
151 const char* test_path, Manifest::Location manifest_location) {
152 const Extension* extension = InstallEphemeralAppWithSourceAndFlags(
153 GetTestPath(test_path), 1, manifest_location, Extension::NO_FLAGS);
154 EXPECT_TRUE(extension);
155 if (extension)
156 EXPECT_TRUE(extensions::util::IsEphemeralApp(extension->id(), profile()));
157 return extension;
158 }
159
InstallEphemeralApp(const char * test_path)160 const Extension* EphemeralAppTestBase::InstallEphemeralApp(
161 const char* test_path) {
162 return InstallEphemeralApp(test_path, Manifest::INTERNAL);
163 }
164
InstallAndLaunchEphemeralApp(const char * test_path)165 const Extension* EphemeralAppTestBase::InstallAndLaunchEphemeralApp(
166 const char* test_path) {
167 ExtensionTestMessageListener launched_listener("launched", false);
168 const Extension* extension = InstallEphemeralApp(test_path);
169 EXPECT_TRUE(extension);
170 if (!extension)
171 return NULL;
172
173 LaunchPlatformApp(extension);
174 bool wait_result = launched_listener.WaitUntilSatisfied();
175 EXPECT_TRUE(wait_result);
176 if (!wait_result)
177 return NULL;
178
179 return extension;
180 }
181
UpdateEphemeralApp(const std::string & app_id,const base::FilePath & test_dir,const base::FilePath & pem_path)182 const Extension* EphemeralAppTestBase::UpdateEphemeralApp(
183 const std::string& app_id,
184 const base::FilePath& test_dir,
185 const base::FilePath& pem_path) {
186 // Pack a new version of the app.
187 base::ScopedTempDir temp_dir;
188 EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
189
190 base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx");
191 if (!base::DeleteFile(crx_path, false)) {
192 ADD_FAILURE() << "Failed to delete existing crx: " << crx_path.value();
193 return NULL;
194 }
195
196 base::FilePath app_v2_path = PackExtensionWithOptions(
197 test_dir, crx_path, pem_path, base::FilePath());
198 EXPECT_FALSE(app_v2_path.empty());
199
200 // Update the ephemeral app and wait for the update to finish.
201 extensions::CrxInstaller* crx_installer = NULL;
202 content::WindowedNotificationObserver windowed_observer(
203 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
204 content::Source<extensions::CrxInstaller>(crx_installer));
205 ExtensionService* service =
206 ExtensionSystem::Get(profile())->extension_service();
207 EXPECT_TRUE(service->UpdateExtension(app_id, app_v2_path, true,
208 &crx_installer));
209 windowed_observer.Wait();
210
211 return service->GetExtensionById(app_id, false);
212 }
213
PromoteEphemeralApp(const extensions::Extension * app)214 void EphemeralAppTestBase::PromoteEphemeralApp(
215 const extensions::Extension* app) {
216 ExtensionService* extension_service =
217 ExtensionSystem::Get(profile())->extension_service();
218 ASSERT_TRUE(extension_service);
219 extension_service->PromoteEphemeralApp(app, false);
220 }
221
CloseApp(const std::string & app_id)222 void EphemeralAppTestBase::CloseApp(const std::string& app_id) {
223 content::WindowedNotificationObserver event_page_destroyed_signal(
224 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
225 content::Source<Profile>(profile()));
226
227 EXPECT_EQ(1U, GetAppWindowCountForApp(app_id));
228 apps::AppWindow* app_window = GetFirstAppWindowForApp(app_id);
229 ASSERT_TRUE(app_window);
230 CloseAppWindow(app_window);
231
232 event_page_destroyed_signal.Wait();
233 }
234
EvictApp(const std::string & app_id)235 void EphemeralAppTestBase::EvictApp(const std::string& app_id) {
236 // Uninstall the app, which is what happens when ephemeral apps get evicted
237 // from the cache.
238 content::WindowedNotificationObserver uninstalled_signal(
239 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
240 content::Source<Profile>(profile()));
241
242 ExtensionService* service =
243 ExtensionSystem::Get(profile())->extension_service();
244 ASSERT_TRUE(service);
245 service->UninstallExtension(app_id, false, NULL);
246
247 uninstalled_signal.Wait();
248 }
249
250 // EphemeralAppBrowserTest:
251
252 class EphemeralAppBrowserTest : public EphemeralAppTestBase {
253 protected:
LaunchAppAndRunTest(const Extension * app,const char * test_name)254 bool LaunchAppAndRunTest(const Extension* app, const char* test_name) {
255 ExtensionTestMessageListener launched_listener("launched", true);
256 LaunchPlatformApp(app);
257 if (!launched_listener.WaitUntilSatisfied()) {
258 message_ = "Failed to receive launched message from test";
259 return false;
260 }
261
262 ResultCatcher catcher;
263 launched_listener.Reply(test_name);
264
265 bool result = catcher.GetNextResult();
266 message_ = catcher.message();
267
268 CloseApp(app->id());
269 return result;
270 }
271
VerifyAppNotLoaded(const std::string & app_id)272 void VerifyAppNotLoaded(const std::string& app_id) {
273 EXPECT_FALSE(ExtensionSystem::Get(profile())->
274 process_manager()->GetBackgroundHostForExtension(app_id));
275 }
276
DispatchAlarmEvent(EventRouter * event_router,const std::string & app_id)277 void DispatchAlarmEvent(EventRouter* event_router,
278 const std::string& app_id) {
279 alarms::Alarm dummy_alarm;
280 dummy_alarm.name = "test_alarm";
281
282 scoped_ptr<base::ListValue> args(new base::ListValue());
283 args->Append(dummy_alarm.ToValue().release());
284 scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
285 args.Pass()));
286
287 event_router->DispatchEventToExtension(app_id, event.Pass());
288 }
289
ReplaceEphemeralApp(const std::string & app_id,const char * test_path)290 const Extension* ReplaceEphemeralApp(const std::string& app_id,
291 const char* test_path) {
292 return UpdateExtensionWaitForIdle(app_id, GetTestPath(test_path), 0);
293 }
294
VerifyPromotedApp(const std::string & app_id,ExtensionRegistry::IncludeFlag expected_set)295 void VerifyPromotedApp(const std::string& app_id,
296 ExtensionRegistry::IncludeFlag expected_set) {
297 const Extension* app = ExtensionRegistry::Get(profile())->GetExtensionById(
298 app_id, expected_set);
299 ASSERT_TRUE(app);
300
301 // The app should not be ephemeral.
302 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
303 ASSERT_TRUE(prefs);
304 EXPECT_FALSE(prefs->IsEphemeralApp(app_id));
305
306 // Check sort ordinals.
307 extensions::AppSorting* app_sorting = prefs->app_sorting();
308 EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).IsValid());
309 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).IsValid());
310 }
311
InitSyncService()312 void InitSyncService() {
313 ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile());
314 sync_service->MergeDataAndStartSyncing(
315 syncer::APPS,
316 syncer::SyncDataList(),
317 scoped_ptr<syncer::SyncChangeProcessor>(
318 new syncer::SyncChangeProcessorWrapperForTest(
319 &mock_sync_processor_)),
320 scoped_ptr<syncer::SyncErrorFactory>(
321 new syncer::SyncErrorFactoryMock()));
322 }
323
GetFirstSyncChangeForApp(const std::string & id)324 scoped_ptr<AppSyncData> GetFirstSyncChangeForApp(const std::string& id) {
325 scoped_ptr<AppSyncData> sync_data;
326 for (syncer::SyncChangeList::iterator it =
327 mock_sync_processor_.changes().begin();
328 it != mock_sync_processor_.changes().end(); ++it) {
329 sync_data.reset(new AppSyncData(*it));
330 if (sync_data->id() == id)
331 return sync_data.Pass();
332 }
333
334 return scoped_ptr<AppSyncData>();
335 }
336
VerifySyncChange(const AppSyncData * sync_change,bool expect_enabled)337 void VerifySyncChange(const AppSyncData* sync_change, bool expect_enabled) {
338 ASSERT_TRUE(sync_change);
339 EXPECT_TRUE(sync_change->page_ordinal().IsValid());
340 EXPECT_TRUE(sync_change->app_launch_ordinal().IsValid());
341 EXPECT_FALSE(sync_change->uninstalled());
342 EXPECT_EQ(expect_enabled, sync_change->extension_sync_data().enabled());
343 }
344
345 syncer::FakeSyncChangeProcessor mock_sync_processor_;
346 };
347
348 // Verify that ephemeral apps can be launched and receive system events when
349 // they are running. Once they are inactive they should not receive system
350 // events.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,EventDispatchWhenLaunched)351 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, EventDispatchWhenLaunched) {
352 const Extension* extension =
353 InstallAndLaunchEphemeralApp(kDispatchEventTestApp);
354 ASSERT_TRUE(extension);
355
356 // Send a fake alarm event to the app and verify that a response is
357 // received.
358 EventRouter* event_router = EventRouter::Get(profile());
359 ASSERT_TRUE(event_router);
360
361 ExtensionTestMessageListener alarm_received_listener("alarm_received", false);
362 DispatchAlarmEvent(event_router, extension->id());
363 ASSERT_TRUE(alarm_received_listener.WaitUntilSatisfied());
364
365 CloseApp(extension->id());
366
367 // The app needs to be launched once in order to have the onAlarm() event
368 // registered.
369 ASSERT_TRUE(event_router->ExtensionHasEventListener(
370 extension->id(), alarms::OnAlarm::kEventName));
371
372 // Dispatch the alarm event again and verify that the event page did not get
373 // loaded for the app.
374 DispatchAlarmEvent(event_router, extension->id());
375 VerifyAppNotLoaded(extension->id());
376 }
377
378 // Verify that ephemeral apps will receive messages while they are running.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,ReceiveMessagesWhenLaunched)379 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, ReceiveMessagesWhenLaunched) {
380 const Extension* receiver =
381 InstallAndLaunchEphemeralApp(kMessagingReceiverApp);
382 ASSERT_TRUE(receiver);
383
384 // Verify that messages are received while the app is running.
385 ExtensionApiTest::ResultCatcher result_catcher;
386 LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_success",
387 "Launched");
388 EXPECT_TRUE(result_catcher.GetNextResult());
389
390 CloseApp(receiver->id());
391
392 // Verify that messages are not received while the app is inactive.
393 LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_fail", "Launched");
394 EXPECT_TRUE(result_catcher.GetNextResult());
395 }
396
397 // Verify that an updated ephemeral app will still have its ephemeral flag
398 // enabled.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,UpdateEphemeralApp)399 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, UpdateEphemeralApp) {
400 const Extension* app_v1 = InstallEphemeralApp(kMessagingReceiverApp);
401 ASSERT_TRUE(app_v1);
402 std::string app_id = app_v1->id();
403 base::Version app_original_version = *app_v1->version();
404 app_v1 = NULL; // The extension object will be destroyed during update.
405
406 // Update to version 2 of the app.
407 InstallObserver installed_observer(profile());
408 const Extension* app_v2 = UpdateEphemeralApp(
409 app_id, GetTestPath(kMessagingReceiverAppV2),
410 GetTestPath(kMessagingReceiverApp).ReplaceExtension(
411 FILE_PATH_LITERAL(".pem")));
412
413 // Check the notification parameters.
414 const InstallObserver::InstallParameters& params = installed_observer.Last();
415 EXPECT_EQ(app_id, params.id);
416 EXPECT_TRUE(params.is_update);
417 EXPECT_FALSE(params.from_ephemeral);
418
419 // The ephemeral flag should still be enabled.
420 ASSERT_TRUE(app_v2);
421 EXPECT_TRUE(app_v2->version()->CompareTo(app_original_version) > 0);
422 EXPECT_TRUE(extensions::util::IsEphemeralApp(app_v2->id(), profile()));
423 }
424
425 // Verify that if notifications have been disabled for an ephemeral app, it will
426 // remain disabled even after being evicted from the cache.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,StickyNotificationSettings)427 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, StickyNotificationSettings) {
428 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
429 ASSERT_TRUE(app);
430
431 // Disable notifications for this app.
432 DesktopNotificationService* notification_service =
433 DesktopNotificationServiceFactory::GetForProfile(profile());
434 ASSERT_TRUE(notification_service);
435
436 message_center::NotifierId notifier_id(
437 message_center::NotifierId::APPLICATION, app->id());
438 EXPECT_TRUE(notification_service->IsNotifierEnabled(notifier_id));
439 notification_service->SetNotifierEnabled(notifier_id, false);
440 EXPECT_FALSE(notification_service->IsNotifierEnabled(notifier_id));
441
442 // Remove the app.
443 EvictApp(app->id());
444
445 // Reinstall the ephemeral app and verify that notifications remain disabled.
446 app = InstallEphemeralApp(kNotificationsTestApp);
447 ASSERT_TRUE(app);
448 message_center::NotifierId reinstalled_notifier_id(
449 message_center::NotifierId::APPLICATION, app->id());
450 EXPECT_FALSE(notification_service->IsNotifierEnabled(
451 reinstalled_notifier_id));
452 }
453
454 // Verify that only running ephemeral apps will appear in the Notification
455 // Settings UI. Inactive, cached ephemeral apps should not appear.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,IncludeRunningEphemeralAppsInNotifiers)456 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
457 IncludeRunningEphemeralAppsInNotifiers) {
458 message_center::NotifierSettingsProvider* settings_provider =
459 message_center::MessageCenter::Get()->GetNotifierSettingsProvider();
460 // TODO(tmdiep): Remove once notifications settings are supported across
461 // all platforms. This test will fail for Linux GTK.
462 if (!settings_provider)
463 return;
464
465 const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp);
466 ASSERT_TRUE(app);
467 message_center::NotifierId notifier_id(
468 message_center::NotifierId::APPLICATION, app->id());
469
470 // Since the ephemeral app is running, it should be included in the list
471 // of notifiers to show in the UI.
472 NotifierList notifiers;
473 STLElementDeleter<NotifierList> notifier_deleter(¬ifiers);
474
475 settings_provider->GetNotifierList(¬ifiers);
476 EXPECT_TRUE(IsNotifierInList(notifier_id, notifiers));
477 STLDeleteElements(¬ifiers);
478
479 // Close the ephemeral app.
480 CloseApp(app->id());
481
482 // Inactive ephemeral apps should not be included in the list of notifiers to
483 // show in the UI.
484 settings_provider->GetNotifierList(¬ifiers);
485 EXPECT_FALSE(IsNotifierInList(notifier_id, notifiers));
486 }
487
488 // Verify that ephemeral apps will have no ability to retain file entries after
489 // close. Normal retainEntry behavior for installed apps is tested in
490 // FileSystemApiTest.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,DisableRetainFileSystemEntries)491 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
492 DisableRetainFileSystemEntries) {
493 // Create a dummy file that we can just return to the test.
494 base::ScopedTempDir temp_dir;
495 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
496 base::FilePath temp_file;
497 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file));
498
499 using extensions::FileSystemChooseEntryFunction;
500 FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
501 &temp_file);
502 // The temporary file needs to be registered for the tests to pass on
503 // ChromeOS.
504 FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
505 "temp", temp_dir.path());
506
507 // The first test opens the file and writes the file handle to local storage.
508 const Extension* app = InstallEphemeralApp(kFileSystemTestApp,
509 Manifest::UNPACKED);
510 ASSERT_TRUE(LaunchAppAndRunTest(app, "OpenAndRetainFile")) << message_;
511
512 // Verify that after the app has been closed, all retained entries are
513 // flushed.
514 std::vector<apps::SavedFileEntry> file_entries =
515 apps::SavedFilesService::Get(profile())
516 ->GetAllFileEntries(app->id());
517 EXPECT_TRUE(file_entries.empty());
518
519 // The second test verifies that the file cannot be reopened.
520 ASSERT_TRUE(LaunchAppAndRunTest(app, "RestoreRetainedFile")) << message_;
521 }
522
523 // Checks the process of installing and then promoting an ephemeral app.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,PromoteEphemeralApp)524 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralApp) {
525 InitSyncService();
526
527 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
528 ASSERT_TRUE(app);
529
530 // Ephemeral apps should not be synced.
531 scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
532 EXPECT_FALSE(sync_change.get());
533
534 // Promote the app to a regular installed app.
535 InstallObserver installed_observer(profile());
536 PromoteEphemeralApp(app);
537 VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED);
538
539 // Check the notification parameters.
540 const InstallObserver::InstallParameters& params = installed_observer.Last();
541 EXPECT_EQ(app->id(), params.id);
542 EXPECT_TRUE(params.is_update);
543 EXPECT_TRUE(params.from_ephemeral);
544
545 // The installation should now be synced.
546 sync_change = GetFirstSyncChangeForApp(app->id());
547 VerifySyncChange(sync_change.get(), true);
548 }
549
550 // Verifies that promoting an ephemeral app will enable it.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,PromoteEphemeralAppAndEnable)551 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppAndEnable) {
552 InitSyncService();
553
554 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
555 ASSERT_TRUE(app);
556
557 // Disable the ephemeral app due to a permissions increase. This also involves
558 // setting the DidExtensionEscalatePermissions flag.
559 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
560 prefs->SetDidExtensionEscalatePermissions(app, true);
561 ExtensionService* service =
562 ExtensionSystem::Get(profile())->extension_service();
563 service->DisableExtension(app->id(), Extension::DISABLE_PERMISSIONS_INCREASE);
564 ASSERT_TRUE(ExtensionRegistry::Get(profile())->
565 GetExtensionById(app->id(), ExtensionRegistry::DISABLED));
566
567 // Promote to a regular installed app. It should be enabled.
568 PromoteEphemeralApp(app);
569 VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED);
570 EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(app->id()));
571
572 scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
573 VerifySyncChange(sync_change.get(), true);
574 }
575
576 // Verifies that promoting an ephemeral app that has unsupported requirements
577 // will not enable it.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,PromoteUnsupportedEphemeralApp)578 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
579 PromoteUnsupportedEphemeralApp) {
580 InitSyncService();
581
582 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
583 ASSERT_TRUE(app);
584
585 // Disable the ephemeral app.
586 ExtensionService* service =
587 ExtensionSystem::Get(profile())->extension_service();
588 service->DisableExtension(
589 app->id(), Extension::DISABLE_UNSUPPORTED_REQUIREMENT);
590 ASSERT_TRUE(ExtensionRegistry::Get(profile())->
591 GetExtensionById(app->id(), ExtensionRegistry::DISABLED));
592
593 // Promote to a regular installed app. It should remain disabled.
594 PromoteEphemeralApp(app);
595 VerifyPromotedApp(app->id(), ExtensionRegistry::DISABLED);
596
597 scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
598 VerifySyncChange(sync_change.get(), false);
599 }
600
601 // Checks the process of promoting an ephemeral app from sync.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,PromoteEphemeralAppFromSync)602 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppFromSync) {
603 InitSyncService();
604
605 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
606 ASSERT_TRUE(app);
607 std::string app_id = app->id();
608
609 // Simulate an install from sync.
610 const syncer::StringOrdinal kAppLaunchOrdinal("x");
611 const syncer::StringOrdinal kPageOrdinal("y");
612 AppSyncData app_sync_data(
613 *app,
614 true /* enabled */,
615 false /* incognito enabled */,
616 false /* remote install */,
617 kAppLaunchOrdinal,
618 kPageOrdinal,
619 extensions::LAUNCH_TYPE_REGULAR);
620
621 ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile());
622 sync_service->ProcessAppSyncData(app_sync_data);
623
624 // Verify the installation.
625 VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
626
627 // The sort ordinals from sync should not be overridden.
628 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
629 extensions::AppSorting* app_sorting = prefs->app_sorting();
630 EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).Equals(
631 kAppLaunchOrdinal));
632 EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).Equals(kPageOrdinal));
633 }
634
635 // In most cases, ExtensionService::PromoteEphemeralApp() will be called to
636 // permanently install an ephemeral app. However, there may be cases where an
637 // install occurs through the usual route of installing from the Web Store (due
638 // to race conditions). Ensure that the app is still installed correctly.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,ReplaceEphemeralAppWithInstalledApp)639 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
640 ReplaceEphemeralAppWithInstalledApp) {
641 const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
642 ASSERT_TRUE(app);
643 std::string app_id = app->id();
644 app = NULL;
645
646 InstallObserver installed_observer(profile());
647 ReplaceEphemeralApp(app_id, kNotificationsTestApp);
648 VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
649
650 // Check the notification parameters.
651 const InstallObserver::InstallParameters& params = installed_observer.Last();
652 EXPECT_EQ(app_id, params.id);
653 EXPECT_TRUE(params.is_update);
654 EXPECT_TRUE(params.from_ephemeral);
655 }
656
657 // This is similar to ReplaceEphemeralAppWithInstalledApp, but installs will
658 // be delayed until the app is idle.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,ReplaceEphemeralAppWithDelayedInstalledApp)659 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
660 ReplaceEphemeralAppWithDelayedInstalledApp) {
661 const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp);
662 ASSERT_TRUE(app);
663 std::string app_id = app->id();
664 app = NULL;
665
666 // Initiate install.
667 ReplaceEphemeralApp(app_id, kNotificationsTestApp);
668
669 // The delayed installation will occur when the ephemeral app is closed.
670 content::WindowedNotificationObserver installed_signal(
671 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
672 content::Source<Profile>(profile()));
673 InstallObserver installed_observer(profile());
674 CloseApp(app_id);
675 installed_signal.Wait();
676 VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
677
678 // Check the notification parameters.
679 const InstallObserver::InstallParameters& params = installed_observer.Last();
680 EXPECT_EQ(app_id, params.id);
681 EXPECT_TRUE(params.is_update);
682 EXPECT_TRUE(params.from_ephemeral);
683 }
684