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 "base/message_loop/message_loop_proxy.h"
6#include "chrome/browser/apps/ephemeral_app_launcher.h"
7#include "chrome/browser/apps/ephemeral_app_service.h"
8#include "chrome/browser/extensions/extension_install_checker.h"
9#include "chrome/browser/extensions/extension_service.h"
10#include "chrome/browser/extensions/install_tracker.h"
11#include "chrome/browser/extensions/test_blacklist.h"
12#include "chrome/browser/extensions/webstore_installer_test.h"
13#include "chrome/browser/ui/browser_finder.h"
14#include "chrome/browser/ui/tabs/tab_strip_model.h"
15#include "chrome/common/chrome_switches.h"
16#include "content/public/browser/web_contents.h"
17#include "content/public/test/test_utils.h"
18#include "extensions/browser/extension_prefs.h"
19#include "extensions/browser/extension_registry.h"
20#include "extensions/browser/extension_system.h"
21#include "extensions/browser/extension_util.h"
22#include "extensions/browser/management_policy.h"
23#include "extensions/browser/process_manager.h"
24#include "extensions/common/switches.h"
25#include "extensions/test/extension_test_message_listener.h"
26
27using extensions::Extension;
28using extensions::ExtensionPrefs;
29using extensions::ExtensionRegistry;
30using extensions::ExtensionSystem;
31using extensions::InstallTracker;
32namespace webstore_install = extensions::webstore_install;
33
34namespace {
35
36const char kWebstoreDomain[] = "cws.com";
37const char kAppDomain[] = "app.com";
38const char kNonAppDomain[] = "nonapp.com";
39const char kTestDataPath[] = "extensions/platform_apps/ephemeral_launcher";
40
41const char kExtensionId[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeid";
42const char kExtensionTestPath[] = "extension";
43const char kLegacyAppId[] = "lnbochkobjfnhbnbljgfgokadhmbahcn";
44const char kLegacyAppTestPath[] = "legacy_app";
45const char kNonExistentId[] = "baaaaaaaaaaaaaaaaaaaaaaaaaaaadid";
46const char kDefaultAppId[] = "kbiancnbopdghkfedjhfdoegjadfjeal";
47const char kDefaultAppCrxFilename[] = "app.crx";
48const char kDefaultAppTestPath[] = "app";
49const char kAppWithPermissionsId[] = "mbfcnecjknjpipkfkoangpfnhhlpamki";
50const char kAppWithPermissionsFilename[] = "app_with_permissions.crx";
51const char kHostedAppId[] = "haaaaaaaaaaaaaaaaaaaaaaaaaaappid";
52const char kHostedAppLaunchUrl[] = "http://foo.bar.com";
53
54class ExtensionInstallCheckerMock : public extensions::ExtensionInstallChecker {
55 public:
56  ExtensionInstallCheckerMock(Profile* profile,
57                              const std::string& requirements_error)
58      : extensions::ExtensionInstallChecker(profile),
59        requirements_error_(requirements_error) {}
60
61  virtual ~ExtensionInstallCheckerMock() {}
62
63 private:
64  virtual void CheckRequirements() OVERRIDE {
65    // Simulate an asynchronous operation.
66    base::MessageLoopProxy::current()->PostTask(
67        FROM_HERE,
68        base::Bind(&ExtensionInstallCheckerMock::RequirementsErrorCheckDone,
69                   base::Unretained(this),
70                   current_sequence_number()));
71  }
72
73  void RequirementsErrorCheckDone(int sequence_number) {
74    std::vector<std::string> errors;
75    errors.push_back(requirements_error_);
76    OnRequirementsCheckDone(sequence_number, errors);
77  }
78
79  std::string requirements_error_;
80};
81
82class EphemeralAppLauncherForTest : public EphemeralAppLauncher {
83 public:
84  EphemeralAppLauncherForTest(const std::string& id,
85                              Profile* profile,
86                              const LaunchCallback& callback)
87      : EphemeralAppLauncher(id, profile, NULL, callback),
88        install_initiated_(false),
89        install_prompt_created_(false) {}
90
91  EphemeralAppLauncherForTest(const std::string& id, Profile* profile)
92      : EphemeralAppLauncher(id, profile, NULL, LaunchCallback()),
93        install_initiated_(false),
94        install_prompt_created_(false) {}
95
96  bool install_initiated() const { return install_initiated_; }
97  bool install_prompt_created() const { return install_prompt_created_; }
98
99  void set_requirements_error(const std::string& error) {
100    requirements_check_error_ = error;
101  }
102
103 private:
104  // Override necessary functions for testing.
105
106  virtual scoped_ptr<extensions::ExtensionInstallChecker> CreateInstallChecker()
107      OVERRIDE {
108    if (requirements_check_error_.empty()) {
109      return EphemeralAppLauncher::CreateInstallChecker();
110    } else {
111      return scoped_ptr<extensions::ExtensionInstallChecker>(
112          new ExtensionInstallCheckerMock(profile(),
113                                          requirements_check_error_));
114    }
115  }
116
117  virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE {
118    install_prompt_created_ = true;
119    return EphemeralAppLauncher::CreateInstallUI();
120  }
121
122  virtual scoped_ptr<extensions::WebstoreInstaller::Approval> CreateApproval()
123      const OVERRIDE {
124    install_initiated_ = true;
125    return EphemeralAppLauncher::CreateApproval();
126  }
127
128 private:
129  virtual ~EphemeralAppLauncherForTest() {}
130  friend class base::RefCountedThreadSafe<EphemeralAppLauncherForTest>;
131
132  mutable bool install_initiated_;
133  std::string requirements_check_error_;
134  bool install_prompt_created_;
135};
136
137class LaunchObserver {
138 public:
139  LaunchObserver()
140      : done_(false),
141        waiting_(false),
142        result_(webstore_install::OTHER_ERROR) {}
143
144  webstore_install::Result result() const { return result_; }
145  const std::string& error() const { return error_; }
146
147  void OnLaunchCallback(webstore_install::Result result,
148                        const std::string& error) {
149    result_ = result;
150    error_ = error;
151    done_ = true;
152    if (waiting_) {
153      waiting_ = false;
154      base::MessageLoopForUI::current()->Quit();
155    }
156  }
157
158  void Wait() {
159    if (done_)
160      return;
161
162    waiting_ = true;
163    content::RunMessageLoop();
164  }
165
166 private:
167  bool done_;
168  bool waiting_;
169  webstore_install::Result result_;
170  std::string error_;
171};
172
173class ManagementPolicyMock : public extensions::ManagementPolicy::Provider {
174 public:
175  ManagementPolicyMock() {}
176
177  virtual std::string GetDebugPolicyProviderName() const OVERRIDE {
178    return "ManagementPolicyMock";
179  }
180
181  virtual bool UserMayLoad(const Extension* extension,
182                           base::string16* error) const OVERRIDE {
183    return false;
184  }
185};
186
187}  // namespace
188
189class EphemeralAppLauncherTest : public WebstoreInstallerTest {
190 public:
191  EphemeralAppLauncherTest()
192      : WebstoreInstallerTest(kWebstoreDomain,
193                              kTestDataPath,
194                              kDefaultAppCrxFilename,
195                              kAppDomain,
196                              kNonAppDomain) {}
197
198  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
199    WebstoreInstallerTest::SetUpCommandLine(command_line);
200
201    // Make event pages get suspended immediately.
202    extensions::ProcessManager::SetEventPageIdleTimeForTesting(1);
203    extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1);
204
205    // Enable ephemeral apps flag.
206    command_line->AppendSwitch(switches::kEnableEphemeralApps);
207  }
208
209  virtual void SetUpOnMainThread() OVERRIDE {
210    WebstoreInstallerTest::SetUpOnMainThread();
211
212    // Disable ephemeral apps immediately after they stop running in tests.
213    EphemeralAppService::Get(profile())->set_disable_delay_for_test(0);
214  }
215
216  base::FilePath GetTestPath(const char* test_name) {
217    return test_data_dir_.AppendASCII("platform_apps/ephemeral_launcher")
218        .AppendASCII(test_name);
219  }
220
221  const Extension* GetInstalledExtension(const std::string& id) {
222    return ExtensionRegistry::Get(profile())
223        ->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
224  }
225
226  void SetCrxFilename(const std::string& filename) {
227    GURL crx_url = GenerateTestServerUrl(kWebstoreDomain, filename);
228    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
229        switches::kAppsGalleryUpdateURL, crx_url.spec());
230  }
231
232  void StartLauncherAndCheckResult(EphemeralAppLauncherForTest* launcher,
233                                   webstore_install::Result expected_result,
234                                   bool expect_install_initiated) {
235    ExtensionTestMessageListener launched_listener("launched", false);
236    LaunchObserver launch_observer;
237
238    launcher->launch_callback_ = base::Bind(&LaunchObserver::OnLaunchCallback,
239                                            base::Unretained(&launch_observer));
240    launcher->Start();
241    launch_observer.Wait();
242
243    // Verify the launch result.
244    EXPECT_EQ(expected_result, launch_observer.result());
245    EXPECT_EQ(expect_install_initiated, launcher->install_initiated());
246
247    // Verify that the app was actually launched if the launcher succeeded.
248    if (launch_observer.result() == webstore_install::SUCCESS)
249      EXPECT_TRUE(launched_listener.WaitUntilSatisfied());
250    else
251      EXPECT_FALSE(launched_listener.was_satisfied());
252
253    // Check the reference count to ensure the launcher instance will not be
254    // leaked.
255    EXPECT_TRUE(launcher->HasOneRef());
256  }
257
258  void RunLaunchTest(const std::string& id,
259                     webstore_install::Result expected_result,
260                     bool expect_install_initiated) {
261    InstallTracker* tracker = InstallTracker::Get(profile());
262    ASSERT_TRUE(tracker);
263    bool was_install_active = !!tracker->GetActiveInstall(id);
264
265    scoped_refptr<EphemeralAppLauncherForTest> launcher(
266        new EphemeralAppLauncherForTest(id, profile()));
267    StartLauncherAndCheckResult(
268        launcher.get(), expected_result, expect_install_initiated);
269
270    // Verify that the install was deregistered from the InstallTracker.
271    EXPECT_EQ(was_install_active, !!tracker->GetActiveInstall(id));
272  }
273
274  void ValidateAppInstalledEphemerally(const std::string& id) {
275    EXPECT_TRUE(GetInstalledExtension(id));
276    EXPECT_TRUE(extensions::util::IsEphemeralApp(id, profile()));
277  }
278
279  const Extension* InstallAndDisableApp(
280      const char* test_path,
281      Extension::DisableReason disable_reason) {
282    const Extension* app = InstallExtension(GetTestPath(test_path), 1);
283    EXPECT_TRUE(app);
284    if (!app)
285      return NULL;
286
287    ExtensionService* service =
288        ExtensionSystem::Get(profile())->extension_service();
289    service->DisableExtension(app->id(), disable_reason);
290
291    if (disable_reason == Extension::DISABLE_PERMISSIONS_INCREASE) {
292      // When an extension is disabled due to a permissions increase, this
293      // flag needs to be set too, for some reason.
294      ExtensionPrefs::Get(profile())
295          ->SetDidExtensionEscalatePermissions(app, true);
296    }
297
298    EXPECT_TRUE(
299        ExtensionRegistry::Get(profile())->disabled_extensions().Contains(
300            app->id()));
301    return app;
302  }
303};
304
305class EphemeralAppLauncherTestDisabled : public EphemeralAppLauncherTest {
306 public:
307  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
308    // Skip EphemeralAppLauncherTest as it enables the feature.
309    WebstoreInstallerTest::SetUpCommandLine(command_line);
310  }
311};
312
313// Verifies that an ephemeral app will not be installed and launched if the
314// feature is disabled.
315IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTestDisabled, FeatureDisabled) {
316  RunLaunchTest(
317      kDefaultAppCrxFilename, webstore_install::LAUNCH_FEATURE_DISABLED, false);
318  EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
319}
320
321// Verifies that an app with no permission warnings will be installed
322// ephemerally and launched without prompting the user.
323IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
324                       LaunchAppWithNoPermissionWarnings) {
325  content::WindowedNotificationObserver unloaded_signal(
326      extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
327      content::Source<Profile>(profile()));
328
329  scoped_refptr<EphemeralAppLauncherForTest> launcher(
330      new EphemeralAppLauncherForTest(kDefaultAppId, profile()));
331  StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true);
332  ValidateAppInstalledEphemerally(kDefaultAppId);
333
334  // Apps with no permission warnings should not result in a prompt.
335  EXPECT_FALSE(launcher->install_prompt_created());
336
337  // Ephemeral apps are unloaded after they stop running.
338  unloaded_signal.Wait();
339
340  // After an app has been installed ephemerally, it can be launched again
341  // without installing from the web store.
342  RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, false);
343}
344
345// Verifies that an app with permission warnings will be installed
346// ephemerally and launched if accepted by the user.
347IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
348                       LaunchAppWithPermissionsWarnings) {
349  SetCrxFilename(kAppWithPermissionsFilename);
350  AutoAcceptInstall();
351
352  scoped_refptr<EphemeralAppLauncherForTest> launcher(
353      new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile()));
354  StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true);
355  ValidateAppInstalledEphemerally(kAppWithPermissionsId);
356  EXPECT_TRUE(launcher->install_prompt_created());
357}
358
359// Verifies that an app with permission warnings will not be installed
360// ephemerally if cancelled by the user.
361IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
362                       CancelInstallAppWithPermissionWarnings) {
363  SetCrxFilename(kAppWithPermissionsFilename);
364  AutoCancelInstall();
365
366  scoped_refptr<EphemeralAppLauncherForTest> launcher(
367      new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile()));
368  StartLauncherAndCheckResult(
369      launcher.get(), webstore_install::USER_CANCELLED, false);
370  EXPECT_FALSE(GetInstalledExtension(kAppWithPermissionsId));
371  EXPECT_TRUE(launcher->install_prompt_created());
372}
373
374// Verifies that an extension will not be installed ephemerally.
375IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallExtension) {
376  RunLaunchTest(
377      kExtensionId, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, false);
378  EXPECT_FALSE(GetInstalledExtension(kExtensionId));
379}
380
381// Verifies that an already installed extension will not be launched.
382IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchExtension) {
383  const Extension* extension =
384      InstallExtension(GetTestPath(kExtensionTestPath), 1);
385  ASSERT_TRUE(extension);
386  RunLaunchTest(extension->id(),
387                webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
388                false);
389}
390
391// Verifies that a legacy packaged app will not be installed ephemerally.
392IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallLegacyApp) {
393  RunLaunchTest(
394      kLegacyAppId, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, false);
395  EXPECT_FALSE(GetInstalledExtension(kLegacyAppId));
396}
397
398// Verifies that a legacy packaged app that is already installed can be
399// launched.
400IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchLegacyApp) {
401  const Extension* extension =
402      InstallExtension(GetTestPath(kLegacyAppTestPath), 1);
403  ASSERT_TRUE(extension);
404  RunLaunchTest(extension->id(), webstore_install::SUCCESS, false);
405}
406
407// Verifies that a hosted app is not installed. Launch succeeds because we
408// navigate to its launch url.
409IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchHostedApp) {
410  LaunchObserver launch_observer;
411
412  scoped_refptr<EphemeralAppLauncherForTest> launcher(
413      new EphemeralAppLauncherForTest(
414          kHostedAppId,
415          profile(),
416          base::Bind(&LaunchObserver::OnLaunchCallback,
417                     base::Unretained(&launch_observer))));
418  launcher->Start();
419  launch_observer.Wait();
420
421  EXPECT_EQ(webstore_install::SUCCESS, launch_observer.result());
422  EXPECT_FALSE(launcher->install_initiated());
423  EXPECT_FALSE(GetInstalledExtension(kHostedAppId));
424
425  // Verify that a navigation to the launch url was attempted.
426  Browser* browser =
427      FindBrowserWithProfile(profile(), chrome::GetActiveDesktop());
428  ASSERT_TRUE(browser);
429  content::WebContents* web_contents =
430      browser->tab_strip_model()->GetActiveWebContents();
431  ASSERT_TRUE(web_contents);
432  EXPECT_EQ(GURL(kHostedAppLaunchUrl), web_contents->GetVisibleURL());
433}
434
435// Verifies that the EphemeralAppLauncher handles non-existent extension ids.
436IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, NonExistentExtensionId) {
437  RunLaunchTest(
438      kNonExistentId, webstore_install::WEBSTORE_REQUEST_ERROR, false);
439  EXPECT_FALSE(GetInstalledExtension(kNonExistentId));
440}
441
442// Verifies that an app blocked by management policy is not installed
443// ephemerally.
444IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlockedByPolicy) {
445  // Register a provider that blocks the installation of all apps.
446  ManagementPolicyMock policy;
447  ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
448      &policy);
449
450  RunLaunchTest(kDefaultAppId, webstore_install::BLOCKED_BY_POLICY, false);
451  EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
452}
453
454// Verifies that an app blacklisted for malware is not installed ephemerally.
455IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistedForMalware) {
456  // Mock a BLACKLISTED_MALWARE return status.
457  extensions::TestBlacklist blacklist_tester(
458      ExtensionSystem::Get(profile())->blacklist());
459  blacklist_tester.SetBlacklistState(
460      kDefaultAppId, extensions::BLACKLISTED_MALWARE, false);
461
462  RunLaunchTest(kDefaultAppId, webstore_install::BLACKLISTED, false);
463  EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
464}
465
466// Verifies that an app with unknown blacklist status is installed ephemerally
467// and launched.
468IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistStateUnknown) {
469  // Mock a BLACKLISTED_MALWARE return status.
470  extensions::TestBlacklist blacklist_tester(
471      ExtensionSystem::Get(profile())->blacklist());
472  blacklist_tester.SetBlacklistState(
473      kDefaultAppId, extensions::BLACKLISTED_UNKNOWN, false);
474
475  RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, true);
476  ValidateAppInstalledEphemerally(kDefaultAppId);
477}
478
479// Verifies that an app with unsupported requirements is not installed
480// ephemerally.
481IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, UnsupportedRequirements) {
482  scoped_refptr<EphemeralAppLauncherForTest> launcher(
483      new EphemeralAppLauncherForTest(kDefaultAppId, profile()));
484  launcher->set_requirements_error("App has unsupported requirements");
485
486  StartLauncherAndCheckResult(
487      launcher.get(), webstore_install::REQUIREMENT_VIOLATIONS, false);
488  EXPECT_FALSE(GetInstalledExtension(kDefaultAppId));
489}
490
491// Verifies that an app disabled due to permissions increase can be enabled
492// and launched.
493IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableAndLaunchApp) {
494  const Extension* app = InstallAndDisableApp(
495      kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE);
496  ASSERT_TRUE(app);
497
498  AutoAcceptInstall();
499  RunLaunchTest(app->id(), webstore_install::SUCCESS, false);
500}
501
502// Verifies that if the user cancels the enable flow, the app will not be
503// enabled and launched.
504IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableCancelled) {
505  const Extension* app = InstallAndDisableApp(
506      kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE);
507  ASSERT_TRUE(app);
508
509  AutoCancelInstall();
510  RunLaunchTest(app->id(), webstore_install::USER_CANCELLED, false);
511}
512
513// Verifies that an installed app that had been blocked by policy cannot be
514// launched.
515IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchAppBlockedByPolicy) {
516  const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1);
517  ASSERT_TRUE(app);
518
519  // Simulate blocking of the app after it has been installed.
520  ManagementPolicyMock policy;
521  ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
522      &policy);
523  ExtensionSystem::Get(profile())->extension_service()->CheckManagementPolicy();
524
525  RunLaunchTest(app->id(), webstore_install::BLOCKED_BY_POLICY, false);
526}
527
528// Verifies that an installed blacklisted app cannot be launched.
529IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchBlacklistedApp) {
530  const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1);
531  ASSERT_TRUE(app);
532
533  ExtensionService* service =
534      ExtensionSystem::Get(profile())->extension_service();
535  service->BlacklistExtensionForTest(app->id());
536  ASSERT_TRUE(
537      ExtensionRegistry::Get(profile())->blacklisted_extensions().Contains(
538          app->id()));
539
540  RunLaunchTest(app->id(), webstore_install::BLACKLISTED, false);
541}
542
543// Verifies that an installed app with unsupported requirements cannot be
544// launched.
545IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest,
546                       LaunchAppWithUnsupportedRequirements) {
547  const Extension* app = InstallAndDisableApp(
548      kDefaultAppTestPath, Extension::DISABLE_UNSUPPORTED_REQUIREMENT);
549  ASSERT_TRUE(app);
550
551  RunLaunchTest(app->id(), webstore_install::REQUIREMENT_VIOLATIONS, false);
552}
553
554// Verifies that a launch will fail if the app is currently being installed.
555IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallInProgress) {
556  extensions::ActiveInstallData install_data(kDefaultAppId);
557  InstallTracker::Get(profile())->AddActiveInstall(install_data);
558
559  RunLaunchTest(kDefaultAppId, webstore_install::INSTALL_IN_PROGRESS, false);
560}
561
562// Verifies that a launch will fail if a duplicate launch is in progress.
563IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, DuplicateLaunchInProgress) {
564  extensions::ActiveInstallData install_data(kDefaultAppId);
565  install_data.is_ephemeral = true;
566  InstallTracker::Get(profile())->AddActiveInstall(install_data);
567
568  RunLaunchTest(kDefaultAppId, webstore_install::LAUNCH_IN_PROGRESS, false);
569}
570