Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / ephemeral_app_browsertest.cc
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 "apps/saved_files_service.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/stl_util.h"
8 #include "chrome/browser/apps/app_browsertest_util.h"
9 #include "chrome/browser/apps/ephemeral_app_service.h"
10 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_test_message_listener.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/notifications/desktop_notification_service.h"
15 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
16 #include "chrome/common/extensions/api/alarms.h"
17 #include "content/public/test/browser_test.h"
18 #include "content/public/test/test_utils.h"
19 #include "extensions/browser/event_router.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/browser/process_manager.h"
23 #include "extensions/common/switches.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/notifier_settings.h"
26
27 using extensions::Event;
28 using extensions::EventRouter;
29 using extensions::Extension;
30 using extensions::ExtensionInfo;
31 using extensions::ExtensionPrefs;
32 using extensions::ExtensionSystem;
33 using extensions::Manifest;
34 using extensions::PlatformAppBrowserTest;
35
36 namespace {
37
38 namespace alarms = extensions::api::alarms;
39
40 const char kDispatchEventTestApp[] = "ephemeral_apps/dispatch_event";
41 const char kMessagingReceiverApp[] = "ephemeral_apps/messaging_receiver";
42 const char kMessagingReceiverAppV2[] = "ephemeral_apps/messaging_receiver2";
43 const char kNotificationsTestApp[] = "ephemeral_apps/notification_settings";
44 const char kFileSystemTestApp[] = "ephemeral_apps/filesystem_retain_entries";
45 const char kRetainDataApp[] = "ephemeral_apps/retain_data";
46
47 typedef std::vector<message_center::Notifier*> NotifierList;
48
49 bool IsNotifierInList(const message_center::NotifierId& notifier_id,
50                       const NotifierList& notifiers) {
51   for (NotifierList::const_iterator it = notifiers.begin();
52        it != notifiers.end(); ++it) {
53     const message_center::Notifier* notifier = *it;
54     if (notifier->notifier_id == notifier_id)
55       return true;
56   }
57
58   return false;
59 }
60
61 bool IsAppInExtensionsInfo(const ExtensionPrefs::ExtensionsInfo& ext_info,
62                            const std::string& extension_id) {
63   for (size_t i = 0; i < ext_info.size(); ++i) {
64     ExtensionInfo* info = ext_info.at(i).get();
65     if (info->extension_id == extension_id)
66       return true;
67   }
68
69   return false;
70 }
71
72 }  // namespace
73
74 class EphemeralAppBrowserTest : public PlatformAppBrowserTest {
75  protected:
76   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
77     // Skip PlatformAppBrowserTest, which sets different values for the switches
78     // below.
79     ExtensionBrowserTest::SetUpCommandLine(command_line);
80
81     // Make event pages get suspended immediately.
82     command_line->AppendSwitchASCII(
83         extensions::switches::kEventPageIdleTime, "10");
84     command_line->AppendSwitchASCII(
85         extensions::switches::kEventPageSuspendingTime, "10");
86   }
87
88   base::FilePath GetTestPath(const char* test_path) {
89     return test_data_dir_.AppendASCII("platform_apps").AppendASCII(test_path);
90   }
91
92   const Extension* InstallEphemeralApp(const char* test_path,
93                                        Manifest::Location manifest_location) {
94     const Extension* extension =
95         InstallExtensionWithSourceAndFlags(
96             GetTestPath(test_path),
97             1,
98             manifest_location,
99             Extension::IS_EPHEMERAL);
100     return extension;
101   }
102
103   const Extension* InstallEphemeralApp(const char* test_path) {
104     return InstallEphemeralApp(test_path, Manifest::INTERNAL);
105   }
106
107   const Extension* InstallAndLaunchEphemeralApp(const char* test_path) {
108     ExtensionTestMessageListener launched_listener("launched", false);
109     const Extension* extension = InstallEphemeralApp(test_path);
110     EXPECT_TRUE(extension);
111     if (!extension)
112       return NULL;
113
114     LaunchPlatformApp(extension);
115     bool wait_result = launched_listener.WaitUntilSatisfied();
116     EXPECT_TRUE(wait_result);
117     if (!wait_result)
118       return NULL;
119
120     return extension;
121   }
122
123   bool LaunchAppAndRunTest(const Extension* app, const char* test_name) {
124     ExtensionTestMessageListener launched_listener("launched", true);
125     LaunchPlatformApp(app);
126     if (!launched_listener.WaitUntilSatisfied()) {
127       message_ = "Failed to receive launched message from test";
128       return false;
129     }
130
131     launched_listener.Reply(test_name);
132
133     ResultCatcher catcher;
134     if (!catcher.GetNextResult()) {
135       message_ = catcher.message();
136       return false;
137     }
138
139     CloseApp(app->id());
140     return true;
141   }
142
143   void CloseApp(const std::string& app_id) {
144     content::WindowedNotificationObserver event_page_destroyed_signal(
145         chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
146         content::Source<Profile>(browser()->profile()));
147
148     EXPECT_EQ(1U, GetAppWindowCountForApp(app_id));
149     apps::AppWindow* app_window = GetFirstAppWindowForApp(app_id);
150     ASSERT_TRUE(app_window);
151     CloseAppWindow(app_window);
152
153     event_page_destroyed_signal.Wait();
154   }
155
156   void EvictApp(const std::string& app_id) {
157     // Uninstall the app, which is what happens when ephemeral apps get evicted
158     // from the cache.
159     content::WindowedNotificationObserver uninstalled_signal(
160         chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
161         content::Source<Profile>(browser()->profile()));
162
163     ExtensionService* service =
164         ExtensionSystem::Get(browser()->profile())->extension_service();
165     ASSERT_TRUE(service);
166     service->UninstallExtension(app_id, false, NULL);
167
168     uninstalled_signal.Wait();
169   }
170
171   void VerifyAppNotLoaded(const std::string& app_id) {
172     EXPECT_FALSE(ExtensionSystem::Get(browser()->profile())->
173         process_manager()->GetBackgroundHostForExtension(app_id));
174   }
175
176   void DispatchAlarmEvent(EventRouter* event_router,
177                           const std::string& app_id) {
178     alarms::Alarm dummy_alarm;
179     dummy_alarm.name = "test_alarm";
180
181     scoped_ptr<base::ListValue> args(new base::ListValue());
182     args->Append(dummy_alarm.ToValue().release());
183     scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
184                                       args.Pass()));
185
186     event_router->DispatchEventToExtension(app_id, event.Pass());
187   }
188
189   void GarbageCollectData() {
190     EphemeralAppService* service =
191         EphemeralAppService::Get(browser()->profile());
192     ASSERT_TRUE(service);
193     service->GarbageCollectData();
194   }
195 };
196
197 // Verify that ephemeral apps can be launched and receive system events when
198 // they are running. Once they are inactive they should not receive system
199 // events.
200 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, EventDispatchWhenLaunched) {
201   const Extension* extension =
202       InstallAndLaunchEphemeralApp(kDispatchEventTestApp);
203   ASSERT_TRUE(extension);
204
205   // Send a fake alarm event to the app and verify that a response is
206   // received.
207   EventRouter* event_router = EventRouter::Get(browser()->profile());
208   ASSERT_TRUE(event_router);
209
210   ExtensionTestMessageListener alarm_received_listener("alarm_received", false);
211   DispatchAlarmEvent(event_router, extension->id());
212   ASSERT_TRUE(alarm_received_listener.WaitUntilSatisfied());
213
214   CloseApp(extension->id());
215
216   // The app needs to be launched once in order to have the onAlarm() event
217   // registered.
218   ASSERT_TRUE(event_router->ExtensionHasEventListener(
219       extension->id(), alarms::OnAlarm::kEventName));
220
221   // Dispatch the alarm event again and verify that the event page did not get
222   // loaded for the app.
223   DispatchAlarmEvent(event_router, extension->id());
224   VerifyAppNotLoaded(extension->id());
225 }
226
227 // Verify that ephemeral apps will receive messages while they are running.
228 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, ReceiveMessagesWhenLaunched) {
229   const Extension* receiver =
230       InstallAndLaunchEphemeralApp(kMessagingReceiverApp);
231   ASSERT_TRUE(receiver);
232
233   // Verify that messages are received while the app is running.
234   ExtensionApiTest::ResultCatcher result_catcher;
235   LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_success");
236   EXPECT_TRUE(result_catcher.GetNextResult());
237
238   CloseApp(receiver->id());
239
240   // Verify that messages are not received while the app is inactive.
241   LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_fail");
242   EXPECT_TRUE(result_catcher.GetNextResult());
243 }
244
245 // Verify that an updated ephemeral app will still have its ephemeral flag
246 // enabled.
247 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, UpdateEphemeralApp) {
248   const Extension* app_v1 = InstallEphemeralApp(kMessagingReceiverApp);
249   ASSERT_TRUE(app_v1);
250   ASSERT_TRUE(app_v1->is_ephemeral());
251   std::string app_id = app_v1->id();
252   base::Version app_original_version = *app_v1->version();
253   app_v1 = NULL; // The extension object will be destroyed during update.
254
255   // Pack version 2 of the app.
256   base::ScopedTempDir temp_dir;
257   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
258
259   base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx");
260   if (!base::DeleteFile(crx_path, false)) {
261     ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
262     return;
263   }
264
265   base::FilePath app_v2_path = PackExtensionWithOptions(
266       GetTestPath(kMessagingReceiverAppV2),
267       crx_path,
268       GetTestPath(kMessagingReceiverApp).ReplaceExtension(
269           FILE_PATH_LITERAL(".pem")),
270       base::FilePath());
271   ASSERT_FALSE(app_v2_path.empty());
272
273   // Update the ephemeral app and wait for the update to finish.
274   extensions::CrxInstaller* crx_installer = NULL;
275   content::WindowedNotificationObserver windowed_observer(
276       chrome::NOTIFICATION_CRX_INSTALLER_DONE,
277       content::Source<extensions::CrxInstaller>(crx_installer));
278   ExtensionService* service =
279       ExtensionSystem::Get(browser()->profile())->extension_service();
280   EXPECT_TRUE(service->UpdateExtension(app_id, app_v2_path, true,
281                                        &crx_installer));
282   windowed_observer.Wait();
283
284   const Extension* app_v2 = service->GetExtensionById(app_id, false);
285   ASSERT_TRUE(app_v2);
286   EXPECT_TRUE(app_v2->version()->CompareTo(app_original_version) > 0);
287   EXPECT_TRUE(app_v2->is_ephemeral());
288 }
289
290 // Verify that if notifications have been disabled for an ephemeral app, it will
291 // remain disabled even after being evicted from the cache.
292 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, StickyNotificationSettings) {
293   const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
294   ASSERT_TRUE(app);
295
296   // Disable notifications for this app.
297   DesktopNotificationService* notification_service =
298       DesktopNotificationServiceFactory::GetForProfile(browser()->profile());
299   ASSERT_TRUE(notification_service);
300
301   message_center::NotifierId notifier_id(
302       message_center::NotifierId::APPLICATION, app->id());
303   EXPECT_TRUE(notification_service->IsNotifierEnabled(notifier_id));
304   notification_service->SetNotifierEnabled(notifier_id, false);
305   EXPECT_FALSE(notification_service->IsNotifierEnabled(notifier_id));
306
307   // Remove the app.
308   EvictApp(app->id());
309
310   // Reinstall the ephemeral app and verify that notifications remain disabled.
311   app = InstallEphemeralApp(kNotificationsTestApp);
312   ASSERT_TRUE(app);
313   message_center::NotifierId reinstalled_notifier_id(
314       message_center::NotifierId::APPLICATION, app->id());
315   EXPECT_FALSE(notification_service->IsNotifierEnabled(
316       reinstalled_notifier_id));
317 }
318
319 // Verify that only running ephemeral apps will appear in the Notification
320 // Settings UI. Inactive, cached ephemeral apps should not appear.
321 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
322                        IncludeRunningEphemeralAppsInNotifiers) {
323   message_center::NotifierSettingsProvider* settings_provider =
324       message_center::MessageCenter::Get()->GetNotifierSettingsProvider();
325   // TODO(tmdiep): Remove once notifications settings are supported across
326   // all platforms. This test will fail for Linux GTK.
327   if (!settings_provider)
328     return;
329
330   const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp);
331   ASSERT_TRUE(app);
332   message_center::NotifierId notifier_id(
333       message_center::NotifierId::APPLICATION, app->id());
334
335   // Since the ephemeral app is running, it should be included in the list
336   // of notifiers to show in the UI.
337   NotifierList notifiers;
338   STLElementDeleter<NotifierList> notifier_deleter(&notifiers);
339
340   settings_provider->GetNotifierList(&notifiers);
341   EXPECT_TRUE(IsNotifierInList(notifier_id, notifiers));
342   STLDeleteElements(&notifiers);
343
344   // Close the ephemeral app.
345   CloseApp(app->id());
346
347   // Inactive ephemeral apps should not be included in the list of notifiers to
348   // show in the UI.
349   settings_provider->GetNotifierList(&notifiers);
350   EXPECT_FALSE(IsNotifierInList(notifier_id, notifiers));
351 }
352
353 // Verify that ephemeral apps will have no ability to retain file entries after
354 // close. Normal retainEntry behavior for installed apps is tested in
355 // FileSystemApiTest.
356 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
357                        DisableRetainFileSystemEntries) {
358   // Create a dummy file that we can just return to the test.
359   base::ScopedTempDir temp_dir;
360   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
361   base::FilePath temp_file;
362   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file));
363
364   using extensions::FileSystemChooseEntryFunction;
365   FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
366       &temp_file);
367   // The temporary file needs to be registered for the tests to pass on
368   // ChromeOS.
369   FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
370       "temp", temp_dir.path());
371
372   // The first test opens the file and writes the file handle to local storage.
373   const Extension* app = InstallEphemeralApp(kFileSystemTestApp,
374                                              Manifest::UNPACKED);
375   ASSERT_TRUE(LaunchAppAndRunTest(app, "OpenAndRetainFile")) << message_;
376
377   // Verify that after the app has been closed, all retained entries are
378   // flushed.
379   std::vector<apps::SavedFileEntry> file_entries =
380       apps::SavedFilesService::Get(browser()->profile())
381           ->GetAllFileEntries(app->id());
382   EXPECT_TRUE(file_entries.empty());
383
384   // The second test verifies that the file cannot be reopened.
385   ASSERT_TRUE(LaunchAppAndRunTest(app, "RestoreRetainedFile")) << message_;
386 }
387
388 // Verify that once evicted from the cache, the data of ephemeral apps will not
389 // be deleted.
390 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, RetainData) {
391   // Phase 1 - Install the ephemeral app and write data to various storage.
392   const Extension* app = InstallEphemeralApp(kRetainDataApp);
393   ASSERT_TRUE(app);
394   ASSERT_TRUE(LaunchAppAndRunTest(app, "WriteData")) << message_;
395
396   // Sanity check to ensure that the ReadData tests should pass before the app
397   // is removed.
398   ASSERT_TRUE(LaunchAppAndRunTest(app, "ReadData")) << message_;
399
400   // Remove the app.
401   const std::string app_id = app->id();
402   EvictApp(app->id());
403   app = NULL;
404
405   // The app should be in the list of evicted apps.
406   ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
407   ASSERT_TRUE(prefs);
408   scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
409       prefs->GetEvictedEphemeralAppsInfo());
410   EXPECT_TRUE(IsAppInExtensionsInfo(*extensions_info, app_id));
411
412   // The app should not be in the list of installed extensions.
413   extensions_info = prefs->GetInstalledExtensionsInfo();
414   EXPECT_FALSE(IsAppInExtensionsInfo(*extensions_info, app_id));
415
416   // Ensure the evicted app is considered to have isolated storage. This will
417   // prevent its data from getting garbage collected by
418   // ExtensionService::GarbageCollectIsolatedStorage().
419   GURL site_url = extensions::util::GetSiteForExtensionId(
420       app_id, browser()->profile());
421   EXPECT_TRUE(extensions::util::SiteHasIsolatedStorage(
422                   site_url, browser()->profile()));
423
424   // Phase 2 - Reinstall the ephemeral app and verify that data still exists
425   // in the storage.
426   app = InstallEphemeralApp(kRetainDataApp);
427   ASSERT_TRUE(app);
428   EXPECT_TRUE(LaunchAppAndRunTest(app, "ReadData")) << message_;
429
430   // The app should now be in the list of installed extensions, but not in the
431   // list of evicted apps.
432   extensions_info = prefs->GetInstalledExtensionsInfo();
433   EXPECT_TRUE(IsAppInExtensionsInfo(*extensions_info, app_id));
434   extensions_info = prefs->GetEvictedEphemeralAppsInfo();
435   EXPECT_FALSE(IsAppInExtensionsInfo(*extensions_info, app_id));
436 }
437
438 // Verify that the data of regular installed apps are deleted on uninstall.
439 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, RemoveInstalledData) {
440   // Install the ephemeral app and write data to various storage.
441   const Extension* app = InstallPlatformApp(kRetainDataApp);
442   ASSERT_TRUE(app);
443   ASSERT_TRUE(LaunchAppAndRunTest(app, "WriteData")) << message_;
444
445   // Remove the app.
446   const std::string app_id = app->id();
447   EvictApp(app->id());
448   app = NULL;
449
450   // The app should not be in the preferences.
451   ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
452   ASSERT_TRUE(prefs);
453   scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
454       prefs->GetEvictedEphemeralAppsInfo());
455   EXPECT_FALSE(IsAppInExtensionsInfo(*extensions_info, app_id));
456   extensions_info = prefs->GetInstalledExtensionsInfo();
457   EXPECT_FALSE(IsAppInExtensionsInfo(*extensions_info, app_id));
458
459   // Reinstall the app and verify that all data has been reset.
460   app = InstallPlatformApp(kRetainDataApp);
461   ASSERT_TRUE(LaunchAppAndRunTest(app, "DataReset")) << message_;
462 }
463
464 // Verify that once evicted from the cache, ephemeral apps will remain in
465 // extension prefs, but marked as evicted. After garbage collection of data,
466 // both their data and preferences should be removed.
467 IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, GarbageCollectData) {
468   // Create two apps. Both will be evicted from the cache, but the data of
469   // one will be garbage collected.
470   const Extension* evict_app = InstallEphemeralApp(kRetainDataApp);
471   ASSERT_TRUE(evict_app);
472   ASSERT_TRUE(LaunchAppAndRunTest(evict_app, "WriteData")) << message_;
473   std::string evict_app_id = evict_app->id();
474   EvictApp(evict_app_id);
475   evict_app = NULL;
476
477   const Extension* retain_app = InstallEphemeralApp(kDispatchEventTestApp);
478   ASSERT_TRUE(retain_app);
479   std::string retain_app_id = retain_app->id();
480   EvictApp(retain_app_id);
481   retain_app = NULL;
482
483   ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
484   ASSERT_TRUE(prefs);
485
486   // Both apps should be in the list of evicted apps.
487   scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
488       prefs->GetEvictedEphemeralAppsInfo());
489   EXPECT_TRUE(IsAppInExtensionsInfo(*extensions_info, retain_app_id));
490   EXPECT_TRUE(IsAppInExtensionsInfo(*extensions_info, evict_app_id));
491
492   // Set a fake last launch time so that the ephemeral app's data will be
493   // garbage collected.
494   base::Time launch_time =
495       base::Time::Now() - base::TimeDelta::FromDays(
496                               EphemeralAppService::kDataInactiveThreshold + 1);
497   prefs->SetLastLaunchTime(evict_app_id, launch_time);
498   prefs->SetLastLaunchTime(retain_app_id, base::Time::Now());
499
500   // Garbage collect data.
501   GarbageCollectData();
502
503   // The garbage collected app should no longer be in the preferences.
504   extensions_info = prefs->GetEvictedEphemeralAppsInfo();
505   EXPECT_TRUE(IsAppInExtensionsInfo(*extensions_info, retain_app_id));
506   ASSERT_FALSE(IsAppInExtensionsInfo(*extensions_info, evict_app_id));
507
508   // Reinstall the app and verify that all data has been reset.
509   evict_app = InstallEphemeralApp(kRetainDataApp);
510   ASSERT_TRUE(LaunchAppAndRunTest(evict_app, "DataReset")) << message_;
511 }