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.
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/browser/test_extension_registry_observer.h"
25 #include "extensions/test/extension_test_message_listener.h"
27 using extensions::Extension
;
28 using extensions::ExtensionPrefs
;
29 using extensions::ExtensionRegistry
;
30 using extensions::ExtensionSystem
;
31 using extensions::InstallTracker
;
32 namespace webstore_install
= extensions::webstore_install
;
36 const char kWebstoreDomain
[] = "cws.com";
37 const char kAppDomain
[] = "app.com";
38 const char kNonAppDomain
[] = "nonapp.com";
39 const char kTestDataPath
[] = "extensions/platform_apps/ephemeral_launcher";
41 const char kExtensionId
[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeid";
42 const char kExtensionTestPath
[] = "extension";
43 const char kLegacyAppId
[] = "lnbochkobjfnhbnbljgfgokadhmbahcn";
44 const char kLegacyAppTestPath
[] = "legacy_app";
45 const char kNonExistentId
[] = "baaaaaaaaaaaaaaaaaaaaaaaaaaaadid";
46 const char kDefaultAppId
[] = "kbiancnbopdghkfedjhfdoegjadfjeal";
47 const char kDefaultAppCrxFilename
[] = "app.crx";
48 const char kDefaultAppTestPath
[] = "app";
49 const char kAppWithPermissionsId
[] = "mbfcnecjknjpipkfkoangpfnhhlpamki";
50 const char kAppWithPermissionsFilename
[] = "app_with_permissions.crx";
51 const char kHostedAppId
[] = "haaaaaaaaaaaaaaaaaaaaaaaaaaappid";
52 const char kHostedAppLaunchUrl
[] = "http://foo.bar.com";
54 class ExtensionInstallCheckerMock
: public extensions::ExtensionInstallChecker
{
56 ExtensionInstallCheckerMock(Profile
* profile
,
57 const std::string
& requirements_error
)
58 : extensions::ExtensionInstallChecker(profile
),
59 requirements_error_(requirements_error
) {}
61 ~ExtensionInstallCheckerMock() override
{}
64 void CheckRequirements() override
{
65 // Simulate an asynchronous operation.
66 base::MessageLoopProxy::current()->PostTask(
68 base::Bind(&ExtensionInstallCheckerMock::RequirementsErrorCheckDone
,
69 base::Unretained(this),
70 current_sequence_number()));
73 void RequirementsErrorCheckDone(int sequence_number
) {
74 std::vector
<std::string
> errors
;
75 errors
.push_back(requirements_error_
);
76 OnRequirementsCheckDone(sequence_number
, errors
);
79 std::string requirements_error_
;
82 class EphemeralAppLauncherForTest
: public EphemeralAppLauncher
{
84 EphemeralAppLauncherForTest(const std::string
& id
,
86 const LaunchCallback
& callback
)
87 : EphemeralAppLauncher(id
, profile
, NULL
, callback
),
88 install_initiated_(false),
89 install_prompt_created_(false) {}
91 EphemeralAppLauncherForTest(const std::string
& id
, Profile
* profile
)
92 : EphemeralAppLauncher(id
, profile
, NULL
, LaunchCallback()),
93 install_initiated_(false),
94 install_prompt_created_(false) {}
96 bool install_initiated() const { return install_initiated_
; }
97 bool install_prompt_created() const { return install_prompt_created_
; }
99 void set_requirements_error(const std::string
& error
) {
100 requirements_check_error_
= error
;
104 // Override necessary functions for testing.
106 scoped_ptr
<extensions::ExtensionInstallChecker
> CreateInstallChecker()
108 if (requirements_check_error_
.empty()) {
109 return EphemeralAppLauncher::CreateInstallChecker();
111 return scoped_ptr
<extensions::ExtensionInstallChecker
>(
112 new ExtensionInstallCheckerMock(profile(),
113 requirements_check_error_
));
117 scoped_ptr
<ExtensionInstallPrompt
> CreateInstallUI() override
{
118 install_prompt_created_
= true;
119 return EphemeralAppLauncher::CreateInstallUI();
122 scoped_ptr
<extensions::WebstoreInstaller::Approval
> CreateApproval()
124 install_initiated_
= true;
125 return EphemeralAppLauncher::CreateApproval();
129 ~EphemeralAppLauncherForTest() override
{}
130 friend class base::RefCountedThreadSafe
<EphemeralAppLauncherForTest
>;
132 mutable bool install_initiated_
;
133 std::string requirements_check_error_
;
134 bool install_prompt_created_
;
137 class LaunchObserver
{
142 result_(webstore_install::OTHER_ERROR
) {}
144 webstore_install::Result
result() const { return result_
; }
145 const std::string
& error() const { return error_
; }
147 void OnLaunchCallback(webstore_install::Result result
,
148 const std::string
& error
) {
154 base::MessageLoopForUI::current()->Quit();
163 content::RunMessageLoop();
169 webstore_install::Result result_
;
173 class ManagementPolicyMock
: public extensions::ManagementPolicy::Provider
{
175 ManagementPolicyMock() {}
177 std::string
GetDebugPolicyProviderName() const override
{
178 return "ManagementPolicyMock";
181 bool UserMayLoad(const Extension
* extension
,
182 base::string16
* error
) const override
{
189 class EphemeralAppLauncherTest
: public WebstoreInstallerTest
{
191 EphemeralAppLauncherTest()
192 : WebstoreInstallerTest(kWebstoreDomain
,
194 kDefaultAppCrxFilename
,
198 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
199 WebstoreInstallerTest::SetUpCommandLine(command_line
);
201 // Make event pages get suspended immediately.
202 extensions::ProcessManager::SetEventPageIdleTimeForTesting(1);
203 extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1);
205 // Enable ephemeral apps flag.
206 command_line
->AppendSwitch(switches::kEnableEphemeralAppsInWebstore
);
209 void SetUpOnMainThread() override
{
210 WebstoreInstallerTest::SetUpOnMainThread();
212 // Disable ephemeral apps immediately after they stop running in tests.
213 EphemeralAppService::Get(profile())->set_disable_delay_for_test(0);
216 base::FilePath
GetTestPath(const char* test_name
) {
217 return test_data_dir_
.AppendASCII("platform_apps/ephemeral_launcher")
218 .AppendASCII(test_name
);
221 const Extension
* GetInstalledExtension(const std::string
& id
) {
222 return ExtensionRegistry::Get(profile())
223 ->GetExtensionById(id
, ExtensionRegistry::EVERYTHING
);
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());
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
;
238 launcher
->launch_callback_
= base::Bind(&LaunchObserver::OnLaunchCallback
,
239 base::Unretained(&launch_observer
));
241 launch_observer
.Wait();
243 // Verify the launch result.
244 EXPECT_EQ(expected_result
, launch_observer
.result());
245 EXPECT_EQ(expect_install_initiated
, launcher
->install_initiated());
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());
251 EXPECT_FALSE(launched_listener
.was_satisfied());
253 // Check the reference count to ensure the launcher instance will not be
255 EXPECT_TRUE(launcher
->HasOneRef());
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
);
265 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
266 new EphemeralAppLauncherForTest(id
, profile()));
267 StartLauncherAndCheckResult(
268 launcher
.get(), expected_result
, expect_install_initiated
);
270 // Verify that the install was deregistered from the InstallTracker.
271 EXPECT_EQ(was_install_active
, !!tracker
->GetActiveInstall(id
));
274 void ValidateAppInstalledEphemerally(const std::string
& id
) {
275 EXPECT_TRUE(GetInstalledExtension(id
));
276 EXPECT_TRUE(extensions::util::IsEphemeralApp(id
, profile()));
279 const Extension
* InstallAndDisableApp(
280 const char* test_path
,
281 Extension::DisableReason disable_reason
) {
282 const Extension
* app
= InstallExtension(GetTestPath(test_path
), 1);
287 ExtensionService
* service
=
288 ExtensionSystem::Get(profile())->extension_service();
289 service
->DisableExtension(app
->id(), disable_reason
);
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);
299 ExtensionRegistry::Get(profile())->disabled_extensions().Contains(
305 class EphemeralAppLauncherTestDisabled
: public EphemeralAppLauncherTest
{
307 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
308 // Skip EphemeralAppLauncherTest as it enables the feature.
309 WebstoreInstallerTest::SetUpCommandLine(command_line
);
313 // Verifies that an ephemeral app will not be installed and launched if the
314 // feature is disabled.
315 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTestDisabled
, FeatureDisabled
) {
317 kDefaultAppCrxFilename
, webstore_install::LAUNCH_FEATURE_DISABLED
, false);
318 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId
));
321 // Verifies that an app with no permission warnings will be installed
322 // ephemerally and launched without prompting the user.
323 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
,
324 LaunchAppWithNoPermissionWarnings
) {
325 extensions::TestExtensionRegistryObserver
observer(
326 ExtensionRegistry::Get(profile()));
328 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
329 new EphemeralAppLauncherForTest(kDefaultAppId
, profile()));
330 StartLauncherAndCheckResult(launcher
.get(), webstore_install::SUCCESS
, true);
331 ValidateAppInstalledEphemerally(kDefaultAppId
);
333 // Apps with no permission warnings should not result in a prompt.
334 EXPECT_FALSE(launcher
->install_prompt_created());
336 // Ephemeral apps are unloaded after they stop running.
337 observer
.WaitForExtensionUnloaded();
339 // After an app has been installed ephemerally, it can be launched again
340 // without installing from the web store.
341 RunLaunchTest(kDefaultAppId
, webstore_install::SUCCESS
, false);
344 // Verifies that an app with permission warnings will be installed
345 // ephemerally and launched if accepted by the user.
346 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
,
347 LaunchAppWithPermissionsWarnings
) {
348 SetCrxFilename(kAppWithPermissionsFilename
);
351 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
352 new EphemeralAppLauncherForTest(kAppWithPermissionsId
, profile()));
353 StartLauncherAndCheckResult(launcher
.get(), webstore_install::SUCCESS
, true);
354 ValidateAppInstalledEphemerally(kAppWithPermissionsId
);
355 EXPECT_TRUE(launcher
->install_prompt_created());
358 // Verifies that an app with permission warnings will not be installed
359 // ephemerally if cancelled by the user.
360 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
,
361 CancelInstallAppWithPermissionWarnings
) {
362 SetCrxFilename(kAppWithPermissionsFilename
);
365 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
366 new EphemeralAppLauncherForTest(kAppWithPermissionsId
, profile()));
367 StartLauncherAndCheckResult(
368 launcher
.get(), webstore_install::USER_CANCELLED
, false);
369 EXPECT_FALSE(GetInstalledExtension(kAppWithPermissionsId
));
370 EXPECT_TRUE(launcher
->install_prompt_created());
373 // Verifies that an extension will not be installed ephemerally.
374 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, InstallExtension
) {
376 kExtensionId
, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE
, false);
377 EXPECT_FALSE(GetInstalledExtension(kExtensionId
));
380 // Verifies that an already installed extension will not be launched.
381 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, LaunchExtension
) {
382 const Extension
* extension
=
383 InstallExtension(GetTestPath(kExtensionTestPath
), 1);
384 ASSERT_TRUE(extension
);
385 RunLaunchTest(extension
->id(),
386 webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE
,
390 // Verifies that a legacy packaged app will not be installed ephemerally.
391 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, InstallLegacyApp
) {
393 kLegacyAppId
, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE
, false);
394 EXPECT_FALSE(GetInstalledExtension(kLegacyAppId
));
397 // Verifies that a legacy packaged app that is already installed can be
399 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, LaunchLegacyApp
) {
400 const Extension
* extension
=
401 InstallExtension(GetTestPath(kLegacyAppTestPath
), 1);
402 ASSERT_TRUE(extension
);
403 RunLaunchTest(extension
->id(), webstore_install::SUCCESS
, false);
406 // Verifies that a hosted app is not installed. Launch succeeds because we
407 // navigate to its launch url.
408 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, LaunchHostedApp
) {
409 LaunchObserver launch_observer
;
411 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
412 new EphemeralAppLauncherForTest(
415 base::Bind(&LaunchObserver::OnLaunchCallback
,
416 base::Unretained(&launch_observer
))));
418 launch_observer
.Wait();
420 EXPECT_EQ(webstore_install::SUCCESS
, launch_observer
.result());
421 EXPECT_FALSE(launcher
->install_initiated());
422 EXPECT_FALSE(GetInstalledExtension(kHostedAppId
));
424 // Verify that a navigation to the launch url was attempted.
426 FindBrowserWithProfile(profile(), chrome::GetActiveDesktop());
427 ASSERT_TRUE(browser
);
428 content::WebContents
* web_contents
=
429 browser
->tab_strip_model()->GetActiveWebContents();
430 ASSERT_TRUE(web_contents
);
431 EXPECT_EQ(GURL(kHostedAppLaunchUrl
), web_contents
->GetVisibleURL());
434 // Verifies that the EphemeralAppLauncher handles non-existent extension ids.
435 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, NonExistentExtensionId
) {
437 kNonExistentId
, webstore_install::WEBSTORE_REQUEST_ERROR
, false);
438 EXPECT_FALSE(GetInstalledExtension(kNonExistentId
));
441 // Verifies that an app blocked by management policy is not installed
443 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, BlockedByPolicy
) {
444 // Register a provider that blocks the installation of all apps.
445 ManagementPolicyMock policy
;
446 ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
449 RunLaunchTest(kDefaultAppId
, webstore_install::BLOCKED_BY_POLICY
, false);
450 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId
));
453 // Verifies that an app blacklisted for malware is not installed ephemerally.
454 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, BlacklistedForMalware
) {
455 // Mock a BLACKLISTED_MALWARE return status.
456 extensions::TestBlacklist
blacklist_tester(
457 extensions::Blacklist::Get(profile()));
458 blacklist_tester
.SetBlacklistState(
459 kDefaultAppId
, extensions::BLACKLISTED_MALWARE
, false);
461 RunLaunchTest(kDefaultAppId
, webstore_install::BLACKLISTED
, false);
462 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId
));
465 // Verifies that an app with unknown blacklist status is installed ephemerally
467 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, BlacklistStateUnknown
) {
468 // Mock a BLACKLISTED_MALWARE return status.
469 extensions::TestBlacklist
blacklist_tester(
470 extensions::Blacklist::Get(profile()));
471 blacklist_tester
.SetBlacklistState(
472 kDefaultAppId
, extensions::BLACKLISTED_UNKNOWN
, false);
474 RunLaunchTest(kDefaultAppId
, webstore_install::SUCCESS
, true);
475 ValidateAppInstalledEphemerally(kDefaultAppId
);
478 // Verifies that an app with unsupported requirements is not installed
480 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, UnsupportedRequirements
) {
481 scoped_refptr
<EphemeralAppLauncherForTest
> launcher(
482 new EphemeralAppLauncherForTest(kDefaultAppId
, profile()));
483 launcher
->set_requirements_error("App has unsupported requirements");
485 StartLauncherAndCheckResult(
486 launcher
.get(), webstore_install::REQUIREMENT_VIOLATIONS
, false);
487 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId
));
490 // Verifies that an app disabled due to permissions increase can be enabled
492 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, EnableAndLaunchApp
) {
493 const Extension
* app
= InstallAndDisableApp(
494 kDefaultAppTestPath
, Extension::DISABLE_PERMISSIONS_INCREASE
);
498 RunLaunchTest(app
->id(), webstore_install::SUCCESS
, false);
501 // Verifies that if the user cancels the enable flow, the app will not be
502 // enabled and launched.
503 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, EnableCancelled
) {
504 const Extension
* app
= InstallAndDisableApp(
505 kDefaultAppTestPath
, Extension::DISABLE_PERMISSIONS_INCREASE
);
509 RunLaunchTest(app
->id(), webstore_install::USER_CANCELLED
, false);
512 // Verifies that an installed app that had been blocked by policy cannot be
514 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, LaunchAppBlockedByPolicy
) {
515 const Extension
* app
= InstallExtension(GetTestPath(kDefaultAppTestPath
), 1);
518 // Simulate blocking of the app after it has been installed.
519 ManagementPolicyMock policy
;
520 ExtensionSystem::Get(profile())->management_policy()->RegisterProvider(
522 ExtensionSystem::Get(profile())->extension_service()->CheckManagementPolicy();
524 RunLaunchTest(app
->id(), webstore_install::BLOCKED_BY_POLICY
, false);
527 // Verifies that an installed blacklisted app cannot be launched.
528 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, LaunchBlacklistedApp
) {
529 const Extension
* app
= InstallExtension(GetTestPath(kDefaultAppTestPath
), 1);
532 ExtensionService
* service
=
533 ExtensionSystem::Get(profile())->extension_service();
534 service
->BlacklistExtensionForTest(app
->id());
536 ExtensionRegistry::Get(profile())->blacklisted_extensions().Contains(
539 RunLaunchTest(app
->id(), webstore_install::BLACKLISTED
, false);
542 // Verifies that an installed app with unsupported requirements cannot be
544 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
,
545 LaunchAppWithUnsupportedRequirements
) {
546 const Extension
* app
= InstallAndDisableApp(
547 kDefaultAppTestPath
, Extension::DISABLE_UNSUPPORTED_REQUIREMENT
);
550 RunLaunchTest(app
->id(), webstore_install::REQUIREMENT_VIOLATIONS
, false);
553 // Verifies that a launch will fail if the app is currently being installed.
554 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, InstallInProgress
) {
555 extensions::ActiveInstallData
install_data(kDefaultAppId
);
556 InstallTracker::Get(profile())->AddActiveInstall(install_data
);
558 RunLaunchTest(kDefaultAppId
, webstore_install::INSTALL_IN_PROGRESS
, false);
561 // Verifies that a launch will fail if a duplicate launch is in progress.
562 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest
, DuplicateLaunchInProgress
) {
563 extensions::ActiveInstallData
install_data(kDefaultAppId
);
564 install_data
.is_ephemeral
= true;
565 InstallTracker::Get(profile())->AddActiveInstall(install_data
);
567 RunLaunchTest(kDefaultAppId
, webstore_install::LAUNCH_IN_PROGRESS
, false);