Index: chrome/browser/apps/ephemeral_app_launcher_browsertest.cc |
diff --git a/chrome/browser/apps/ephemeral_app_launcher_browsertest.cc b/chrome/browser/apps/ephemeral_app_launcher_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..059c47e70362399fcb69e30511e73a00720fdb84 |
--- /dev/null |
+++ b/chrome/browser/apps/ephemeral_app_launcher_browsertest.cc |
@@ -0,0 +1,463 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/message_loop/message_loop_proxy.h" |
+#include "chrome/browser/apps/ephemeral_app_launcher.h" |
+#include "chrome/browser/extensions/extension_install_checker.h" |
+#include "chrome/browser/extensions/extension_service.h" |
+#include "chrome/browser/extensions/extension_test_message_listener.h" |
+#include "chrome/browser/extensions/test_blacklist.h" |
+#include "chrome/browser/extensions/webstore_installer_test.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "content/public/test/test_utils.h" |
+#include "extensions/browser/extension_prefs.h" |
+#include "extensions/browser/extension_registry.h" |
+#include "extensions/browser/extension_system.h" |
+#include "extensions/browser/extension_util.h" |
+#include "extensions/browser/management_policy.h" |
+ |
+using extensions::Extension; |
+using extensions::ExtensionPrefs; |
+using extensions::ExtensionRegistry; |
+using extensions::ExtensionSystem; |
+namespace webstore_install = extensions::webstore_install; |
+ |
+namespace { |
+ |
+const char kWebstoreDomain[] = "cws.com"; |
+const char kAppDomain[] = "app.com"; |
+const char kNonAppDomain[] = "nonapp.com"; |
+const char kTestDataPath[] = "extensions/platform_apps/ephemeral_launcher"; |
+ |
+const char kExtensionId[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeid"; |
+const char kExtensionTestPath[] = "extension"; |
+const char kNonExistentId[] = "baaaaaaaaaaaaaaaaaaaaaaaaaaaadid"; |
+const char kDefaultAppId[] = "kbiancnbopdghkfedjhfdoegjadfjeal"; |
+const char kDefaultAppCrxFilename[] = "app.crx"; |
+const char kDefaultAppTestPath[] = "app"; |
+const char kAppWithPermissionsId[] = "mbfcnecjknjpipkfkoangpfnhhlpamki"; |
+const char kAppWithPermissionsFilename[] = "app_with_permissions.crx"; |
+ |
+class ExtensionInstallCheckerMock : public extensions::ExtensionInstallChecker { |
+ public: |
+ ExtensionInstallCheckerMock(Profile* profile, |
+ const std::string& requirements_error) |
+ : extensions::ExtensionInstallChecker(profile), |
+ requirements_error_(requirements_error) {} |
+ |
+ virtual ~ExtensionInstallCheckerMock() {} |
+ |
+ private: |
+ virtual void CheckRequirements() OVERRIDE { |
+ // Simulate an asynchronous operation. |
+ base::MessageLoopProxy::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ExtensionInstallCheckerMock::RequirementsErrorCheckDone, |
+ base::Unretained(this), |
+ current_sequence_number())); |
+ } |
+ |
+ void RequirementsErrorCheckDone(int sequence_number) { |
+ std::vector<std::string> errors; |
+ errors.push_back(requirements_error_); |
+ OnRequirementsCheckDone(sequence_number, errors); |
+ } |
+ |
+ std::string requirements_error_; |
+}; |
+ |
+class EphemeralAppLauncherForTest : public EphemeralAppLauncher { |
+ public: |
+ EphemeralAppLauncherForTest(const std::string& id, Profile* profile) |
+ : EphemeralAppLauncher(id, profile, NULL, LaunchCallback()), |
+ install_initiated_(false), |
+ install_prompt_created_(false) {} |
+ |
+ bool install_initiated() const { return install_initiated_; } |
+ bool install_prompt_created() const { return install_prompt_created_; } |
+ |
+ void set_requirements_error(const std::string& error) { |
+ requirements_check_error_ = error; |
+ } |
+ |
+ private: |
+ // Override necessary functions for testing. |
+ |
+ virtual scoped_ptr<extensions::ExtensionInstallChecker> CreateInstallChecker() |
+ OVERRIDE { |
+ if (requirements_check_error_.empty()) { |
+ return EphemeralAppLauncher::CreateInstallChecker(); |
+ } else { |
+ return scoped_ptr<extensions::ExtensionInstallChecker>( |
+ new ExtensionInstallCheckerMock(profile(), |
+ requirements_check_error_)); |
+ } |
+ } |
+ |
+ virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE { |
+ install_prompt_created_ = true; |
+ return EphemeralAppLauncher::CreateInstallUI(); |
+ } |
+ |
+ virtual scoped_ptr<extensions::WebstoreInstaller::Approval> CreateApproval() |
+ const OVERRIDE { |
+ install_initiated_ = true; |
+ return EphemeralAppLauncher::CreateApproval(); |
+ } |
+ |
+ private: |
+ virtual ~EphemeralAppLauncherForTest() {} |
+ friend class base::RefCountedThreadSafe<EphemeralAppLauncherForTest>; |
+ |
+ mutable bool install_initiated_; |
+ std::string requirements_check_error_; |
+ bool install_prompt_created_; |
+}; |
+ |
+class LaunchObserver { |
+ public: |
+ LaunchObserver() |
+ : done_(false), |
+ waiting_(false), |
+ result_(webstore_install::UNKNOWN_ERROR) {} |
+ |
+ webstore_install::Result result() const { return result_; } |
+ const std::string& error() const { return error_; } |
+ |
+ void OnLaunchCallback(webstore_install::Result result, |
+ const std::string& error) { |
+ result_ = result; |
+ error_ = error; |
+ done_ = true; |
+ if (waiting_) { |
+ waiting_ = false; |
+ base::MessageLoopForUI::current()->Quit(); |
+ } |
+ } |
+ |
+ void Wait() { |
+ if (done_) |
+ return; |
+ |
+ waiting_ = true; |
+ content::RunMessageLoop(); |
+ } |
+ |
+ private: |
+ bool done_; |
+ bool waiting_; |
+ webstore_install::Result result_; |
+ std::string error_; |
+}; |
+ |
+class ManagementPolicyMock : public extensions::ManagementPolicy::Provider { |
+ public: |
+ ManagementPolicyMock() {} |
+ |
+ virtual std::string GetDebugPolicyProviderName() const OVERRIDE { |
+ return "ManagementPolicyMock"; |
+ } |
+ |
+ virtual bool UserMayLoad(const Extension* extension, |
+ base::string16* error) const OVERRIDE { |
+ return false; |
+ } |
+}; |
+ |
+} // namespace |
+ |
+class EphemeralAppLauncherTest : public WebstoreInstallerTest { |
+ public: |
+ EphemeralAppLauncherTest() |
+ : WebstoreInstallerTest(kWebstoreDomain, |
+ kTestDataPath, |
+ kDefaultAppCrxFilename, |
+ kAppDomain, |
+ kNonAppDomain) {} |
+ |
+ virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { |
+ WebstoreInstallerTest::SetUpCommandLine(command_line); |
+ |
+ // Enable ephemeral apps flag. |
+ command_line->AppendSwitch(switches::kEnableEphemeralApps); |
+ } |
+ |
+ base::FilePath GetTestPath(const char* test_name) { |
+ return test_data_dir_.AppendASCII("platform_apps/ephemeral_launcher") |
+ .AppendASCII(test_name); |
+ } |
+ |
+ const Extension* GetInstalledExtension(const std::string& id) { |
+ return ExtensionRegistry::Get(profile()) |
+ ->GetExtensionById(id, ExtensionRegistry::EVERYTHING); |
+ } |
+ |
+ void SetCrxFilename(const std::string& filename) { |
+ GURL crx_url = GenerateTestServerUrl(kWebstoreDomain, filename); |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kAppsGalleryUpdateURL, crx_url.spec()); |
+ } |
+ |
+ void StartLauncherAndCheckResult(EphemeralAppLauncherForTest* launcher, |
+ webstore_install::Result expected_result, |
+ bool expect_install_initiated) { |
+ ExtensionTestMessageListener launched_listener("launched", false); |
+ LaunchObserver launch_observer; |
+ |
+ launcher->launch_callback_ = base::Bind(&LaunchObserver::OnLaunchCallback, |
+ base::Unretained(&launch_observer)); |
+ launcher->Start(); |
+ launch_observer.Wait(); |
+ |
+ // Verify the launch result. |
+ EXPECT_EQ(expected_result, launch_observer.result()); |
+ EXPECT_EQ(expect_install_initiated, launcher->install_initiated()); |
+ |
+ // Verify that the app was actually launched if the launcher succeeded. |
+ if (launch_observer.result() == webstore_install::SUCCESS) |
+ EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); |
+ else |
+ EXPECT_FALSE(launched_listener.was_satisfied()); |
+ |
+ // Check the reference count to ensure the launcher instance will not be |
+ // leaked. |
+ EXPECT_TRUE(launcher->HasOneRef()); |
+ } |
+ |
+ void RunLaunchTest(const std::string& id, |
+ webstore_install::Result expected_result, |
+ bool expect_install_initiated) { |
+ scoped_refptr<EphemeralAppLauncherForTest> launcher( |
+ new EphemeralAppLauncherForTest(id, profile())); |
+ StartLauncherAndCheckResult( |
+ launcher.get(), expected_result, expect_install_initiated); |
+ } |
+ |
+ void ValidateAppInstalledEphemerally(const std::string& id) { |
+ EXPECT_TRUE(GetInstalledExtension(id)); |
+ EXPECT_TRUE(extensions::util::IsEphemeralApp(id, profile())); |
+ } |
+ |
+ const Extension* InstallAndDisableApp( |
+ const char* test_path, |
+ Extension::DisableReason disable_reason) { |
+ const Extension* app = InstallExtension(GetTestPath(test_path), 1); |
+ EXPECT_TRUE(app); |
+ if (!app) |
+ return NULL; |
+ |
+ if (disable_reason == Extension::DISABLE_GREYLIST) { |
+ ExtensionPrefs::Get(profile())->SetExtensionBlacklistState( |
+ app->id(), extensions::BLACKLISTED_MALWARE); |
+ } |
+ |
+ ExtensionService* service = |
+ ExtensionSystem::Get(profile())->extension_service(); |
+ service->DisableExtension(app->id(), disable_reason); |
+ |
+ if (disable_reason == Extension::DISABLE_PERMISSIONS_INCREASE) { |
+ // When an extension is disabled due to a permissions increase, this |
+ // flag needs to be set too, for some reason. |
+ ExtensionPrefs::Get(profile()) |
+ ->SetDidExtensionEscalatePermissions(app, true); |
+ } |
+ |
+ EXPECT_FALSE( |
+ ExtensionRegistry::Get(profile())->enabled_extensions().Contains( |
+ app->id())); |
+ return app; |
+ } |
+}; |
+ |
+class EphemeralAppLauncherTestDisabled : public EphemeralAppLauncherTest { |
+ public: |
+ virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { |
+ // Skip EphemeralAppLauncherTest as it enables the feature. |
+ WebstoreInstallerTest::SetUpCommandLine(command_line); |
+ } |
+}; |
+ |
+// Verifies that an ephemeral app will not be installed and launched if the |
+// feature is disabled. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTestDisabled, FeatureDisabled) { |
+ RunLaunchTest( |
+ kDefaultAppCrxFilename, webstore_install::LAUNCH_FEATURE_DISABLED, false); |
+ EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
+} |
+ |
+// Verifies that an app with no permission warnings will be installed |
+// ephemerally and launched without prompting the user. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
+ LaunchAppWithNoPermissionWarnings) { |
+ scoped_refptr<EphemeralAppLauncherForTest> launcher( |
+ new EphemeralAppLauncherForTest(kDefaultAppId, profile())); |
+ StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true); |
+ ValidateAppInstalledEphemerally(kDefaultAppId); |
+ |
+ // Apps with no permission warnings should not result in a prompt. |
+ EXPECT_FALSE(launcher->install_prompt_created()); |
+ |
+ // After an app has been installed ephemerally, it can be launched again |
+ // without installing from the web store. |
+ RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, false); |
+} |
+ |
+// Verifies that an app with permission warnings will be installed |
+// ephemerally and launched if accepted by the user. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
+ LaunchAppWithPermissionsWarnings) { |
+ SetCrxFilename(kAppWithPermissionsFilename); |
+ AutoAcceptInstall(); |
+ |
+ scoped_refptr<EphemeralAppLauncherForTest> launcher( |
+ new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile())); |
+ StartLauncherAndCheckResult(launcher.get(), webstore_install::SUCCESS, true); |
+ ValidateAppInstalledEphemerally(kAppWithPermissionsId); |
+ EXPECT_TRUE(launcher->install_prompt_created()); |
+} |
+ |
+// Verifies that an app with permission warnings will not be installed |
+// ephemerally if cancelled by the user. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
+ CancelInstallAppWithPermissionWarnings) { |
+ SetCrxFilename(kAppWithPermissionsFilename); |
+ AutoCancelInstall(); |
+ |
+ scoped_refptr<EphemeralAppLauncherForTest> launcher( |
+ new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile())); |
+ StartLauncherAndCheckResult( |
+ launcher.get(), webstore_install::USER_CANCELLED, false); |
+ EXPECT_FALSE(GetInstalledExtension(kAppWithPermissionsId)); |
+ EXPECT_TRUE(launcher->install_prompt_created()); |
+} |
+ |
+// Verifies that an extension will not be installed ephemerally. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallExtension) { |
+ RunLaunchTest( |
+ kExtensionId, webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, false); |
+ EXPECT_FALSE(GetInstalledExtension(kExtensionId)); |
+} |
+ |
+// Verifies that an already installed extension will not be launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchExtension) { |
+ const Extension* extension = |
+ InstallExtension(GetTestPath(kExtensionTestPath), 1); |
+ ASSERT_TRUE(extension); |
+ RunLaunchTest(extension->id(), |
+ webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, |
+ false); |
+} |
+ |
+// Verifies that the EphemeralAppLauncher handles non-existent extension ids. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, NonExistentExtensionId) { |
+ RunLaunchTest( |
+ kNonExistentId, webstore_install::WEBSTORE_REQUEST_ERROR, false); |
+ EXPECT_FALSE(GetInstalledExtension(kNonExistentId)); |
+} |
+ |
+// Verifies that an app blocked by management policy is not installed |
+// ephemerally. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlockedByPolicy) { |
+ // Register a provider that blocks the installation of all apps. |
+ ManagementPolicyMock policy; |
+ ExtensionSystem::Get(profile())->management_policy()->RegisterProvider( |
+ &policy); |
+ |
+ RunLaunchTest(kDefaultAppId, webstore_install::BLOCKED_BY_POLICY, false); |
+ EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
+} |
+ |
+// Verifies that an app blacklisted for malware is not installed ephemerally. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistedForMalware) { |
+ // Mock a BLACKLISTED_MALWARE return status. |
+ extensions::TestBlacklist blacklist_tester( |
+ ExtensionSystem::Get(profile())->blacklist()); |
+ blacklist_tester.SetBlacklistState( |
+ kDefaultAppId, extensions::BLACKLISTED_MALWARE, false); |
+ |
+ RunLaunchTest(kDefaultAppId, webstore_install::BLACKLISTED, false); |
+ EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
+} |
+ |
+// Verifies that an app with unknown blacklist status is installed ephemerally |
+// and launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistStateUnknown) { |
+ // Mock a BLACKLISTED_MALWARE return status. |
+ extensions::TestBlacklist blacklist_tester( |
+ ExtensionSystem::Get(profile())->blacklist()); |
+ blacklist_tester.SetBlacklistState( |
+ kDefaultAppId, extensions::BLACKLISTED_UNKNOWN, false); |
+ |
+ RunLaunchTest(kDefaultAppId, webstore_install::SUCCESS, true); |
+ ValidateAppInstalledEphemerally(kDefaultAppId); |
+} |
+ |
+// Verifies that an app with unsupported requirements is not installed |
+// ephemerally. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, UnsupportedRequirements) { |
+ scoped_refptr<EphemeralAppLauncherForTest> launcher( |
+ new EphemeralAppLauncherForTest(kDefaultAppId, profile())); |
+ launcher->set_requirements_error("App has unsupported requirements"); |
+ |
+ StartLauncherAndCheckResult( |
+ launcher.get(), webstore_install::REQUIREMENT_VIOLATIONS, false); |
+ EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
+} |
+ |
+// Verifies that an app disabled due to permissions increase can be enabled |
+// and launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableAndLaunchApp) { |
+ const Extension* app = InstallAndDisableApp( |
+ kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE); |
+ ASSERT_TRUE(app); |
+ |
+ AutoAcceptInstall(); |
+ RunLaunchTest(app->id(), webstore_install::SUCCESS, false); |
+} |
+ |
+// Verifies that if the user cancels the enable flow, the app will not be |
+// enabled and launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableCancelled) { |
+ const Extension* app = InstallAndDisableApp( |
+ kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE); |
+ ASSERT_TRUE(app); |
+ |
+ AutoCancelInstall(); |
+ RunLaunchTest(app->id(), webstore_install::USER_CANCELLED, false); |
+} |
+ |
+// Verifies that an installed app that had been blocked by policy cannot be |
+// launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchAppBlockedByPolicy) { |
+ const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1); |
+ ASSERT_TRUE(app); |
+ |
+ // Simulate blocking of the app after it has been installed. |
+ ManagementPolicyMock policy; |
+ ExtensionSystem::Get(profile())->management_policy()->RegisterProvider( |
+ &policy); |
+ ExtensionSystem::Get(profile())->extension_service()->CheckManagementPolicy(); |
+ |
+ RunLaunchTest(app->id(), webstore_install::BLOCKED_BY_POLICY, false); |
+} |
+ |
+// Verifies that an installed blacklisted app cannot be launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchBlacklistedApp) { |
+ const Extension* app = |
+ InstallAndDisableApp(kDefaultAppTestPath, Extension::DISABLE_GREYLIST); |
+ ASSERT_TRUE(app); |
+ |
+ RunLaunchTest(app->id(), webstore_install::BLACKLISTED, false); |
+} |
+ |
+// Verifies that an installed app with unsupported requirements cannot be |
+// launched. |
+IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
+ LaunchAppWithUnsupportedRequirements) { |
+ const Extension* app = InstallAndDisableApp( |
+ kDefaultAppTestPath, Extension::DISABLE_UNSUPPORTED_REQUIREMENT); |
+ ASSERT_TRUE(app); |
+ |
+ RunLaunchTest(app->id(), webstore_install::REQUIREMENT_VIOLATIONS, false); |
+} |