OLD | NEW |
(Empty) | |
| 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/extensions/extension_install_checker.h" |
| 8 #include "chrome/browser/extensions/extension_service.h" |
| 9 #include "chrome/browser/extensions/extension_test_message_listener.h" |
| 10 #include "chrome/browser/extensions/test_blacklist.h" |
| 11 #include "chrome/browser/extensions/webstore_installer_test.h" |
| 12 #include "chrome/common/chrome_switches.h" |
| 13 #include "content/public/test/test_utils.h" |
| 14 #include "extensions/browser/extension_prefs.h" |
| 15 #include "extensions/browser/extension_registry.h" |
| 16 #include "extensions/browser/extension_system.h" |
| 17 #include "extensions/browser/extension_util.h" |
| 18 #include "extensions/browser/management_policy.h" |
| 19 |
| 20 using extensions::Extension; |
| 21 using extensions::ExtensionPrefs; |
| 22 using extensions::ExtensionRegistry; |
| 23 using extensions::ExtensionSystem; |
| 24 |
| 25 namespace { |
| 26 |
| 27 const char kWebstoreDomain[] = "cws.com"; |
| 28 const char kAppDomain[] = "app.com"; |
| 29 const char kNonAppDomain[] = "nonapp.com"; |
| 30 const char kTestDataPath[] = "extensions/platform_apps/ephemeral_launcher"; |
| 31 |
| 32 const char kExtensionId[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeid"; |
| 33 const char kExtensionTestPath[] = "extension"; |
| 34 const char kNonExistentId[] = "baaaaaaaaaaaaaaaaaaaaaaaaaaaadid"; |
| 35 const char kDefaultAppId[] = "kbiancnbopdghkfedjhfdoegjadfjeal"; |
| 36 const char kDefaultAppCrxFilename[] = "app.crx"; |
| 37 const char kDefaultAppTestPath[] = "app"; |
| 38 const char kAppWithPermissionsId[] = "mbfcnecjknjpipkfkoangpfnhhlpamki"; |
| 39 const char kAppWithPermissionsFilename[] = "app_with_permissions.crx"; |
| 40 |
| 41 class ExtensionInstallCheckerMock : public extensions::ExtensionInstallChecker { |
| 42 public: |
| 43 ExtensionInstallCheckerMock(Profile* profile, |
| 44 const std::string& requirements_error) |
| 45 : extensions::ExtensionInstallChecker(profile), |
| 46 requirements_error_(requirements_error) {} |
| 47 |
| 48 virtual ~ExtensionInstallCheckerMock() {} |
| 49 |
| 50 private: |
| 51 virtual void CheckRequirements() OVERRIDE { |
| 52 // Simulate an asynchronous operation. |
| 53 base::MessageLoopProxy::current()->PostTask( |
| 54 FROM_HERE, |
| 55 base::Bind(&ExtensionInstallCheckerMock::RequirementsErrorCheckDone, |
| 56 base::Unretained(this))); |
| 57 } |
| 58 |
| 59 void RequirementsErrorCheckDone() { |
| 60 std::vector<std::string> errors; |
| 61 errors.push_back(requirements_error_); |
| 62 OnRequirementsCheckDone(errors); |
| 63 } |
| 64 |
| 65 std::string requirements_error_; |
| 66 }; |
| 67 |
| 68 class EphemeralAppLauncherForTest : public EphemeralAppLauncher { |
| 69 public: |
| 70 EphemeralAppLauncherForTest(const std::string& id, Profile* profile) |
| 71 : EphemeralAppLauncher(id, profile, NULL, LaunchCallback()), |
| 72 install_initiated_(false), |
| 73 install_prompt_created_(false) {} |
| 74 |
| 75 bool install_initiated() const { return install_initiated_; } |
| 76 bool install_prompt_created() const { return install_prompt_created_; } |
| 77 |
| 78 void set_requirements_error(const std::string& error) { |
| 79 requirements_check_error_ = error; |
| 80 } |
| 81 |
| 82 private: |
| 83 // Override necessary functions for testing. |
| 84 |
| 85 virtual scoped_ptr<extensions::ExtensionInstallChecker> CreateInstallChecker() |
| 86 OVERRIDE { |
| 87 if (requirements_check_error_.empty()) { |
| 88 return EphemeralAppLauncher::CreateInstallChecker(); |
| 89 } else { |
| 90 return scoped_ptr<extensions::ExtensionInstallChecker>( |
| 91 new ExtensionInstallCheckerMock(profile(), |
| 92 requirements_check_error_)); |
| 93 } |
| 94 } |
| 95 |
| 96 virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE { |
| 97 install_prompt_created_ = true; |
| 98 return EphemeralAppLauncher::CreateInstallUI(); |
| 99 } |
| 100 |
| 101 virtual scoped_ptr<extensions::WebstoreInstaller::Approval> CreateApproval() |
| 102 const OVERRIDE { |
| 103 install_initiated_ = true; |
| 104 return EphemeralAppLauncher::CreateApproval(); |
| 105 } |
| 106 |
| 107 private: |
| 108 virtual ~EphemeralAppLauncherForTest() {} |
| 109 friend class base::RefCountedThreadSafe<EphemeralAppLauncherForTest>; |
| 110 |
| 111 mutable bool install_initiated_; |
| 112 std::string requirements_check_error_; |
| 113 bool install_prompt_created_; |
| 114 }; |
| 115 |
| 116 class LaunchObserver { |
| 117 public: |
| 118 LaunchObserver() |
| 119 : done_(false), |
| 120 waiting_(false), |
| 121 result_(EphemeralAppLauncher::LAUNCH_UNKNOWN_ERROR) {} |
| 122 |
| 123 EphemeralAppLauncher::LaunchResult result() const { return result_; } |
| 124 const std::string& error() const { return error_; } |
| 125 |
| 126 void OnLaunchCallback(EphemeralAppLauncher::LaunchResult result, |
| 127 const std::string& error) { |
| 128 result_ = result; |
| 129 error_ = error; |
| 130 done_ = true; |
| 131 if (waiting_) { |
| 132 waiting_ = false; |
| 133 base::MessageLoopForUI::current()->Quit(); |
| 134 } |
| 135 } |
| 136 |
| 137 void Wait() { |
| 138 if (done_) |
| 139 return; |
| 140 |
| 141 waiting_ = true; |
| 142 content::RunMessageLoop(); |
| 143 } |
| 144 |
| 145 private: |
| 146 bool done_; |
| 147 bool waiting_; |
| 148 EphemeralAppLauncher::LaunchResult result_; |
| 149 std::string error_; |
| 150 }; |
| 151 |
| 152 class ManagementPolicyMock : public extensions::ManagementPolicy::Provider { |
| 153 public: |
| 154 ManagementPolicyMock() {} |
| 155 |
| 156 virtual std::string GetDebugPolicyProviderName() const OVERRIDE { |
| 157 return "ManagementPolicyMock"; |
| 158 } |
| 159 |
| 160 virtual bool UserMayLoad(const Extension* extension, |
| 161 base::string16* error) const OVERRIDE { |
| 162 return false; |
| 163 } |
| 164 }; |
| 165 |
| 166 } // namespace |
| 167 |
| 168 class EphemeralAppLauncherTest : public WebstoreInstallerTest { |
| 169 public: |
| 170 EphemeralAppLauncherTest() |
| 171 : WebstoreInstallerTest(kWebstoreDomain, |
| 172 kTestDataPath, |
| 173 kDefaultAppCrxFilename, |
| 174 kAppDomain, |
| 175 kNonAppDomain) {} |
| 176 |
| 177 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { |
| 178 WebstoreInstallerTest::SetUpCommandLine(command_line); |
| 179 |
| 180 // Enable ephemeral apps flag. |
| 181 command_line->AppendSwitch(switches::kEnableEphemeralApps); |
| 182 } |
| 183 |
| 184 base::FilePath GetTestPath(const char* test_name) { |
| 185 return test_data_dir_.AppendASCII("platform_apps/ephemeral_launcher") |
| 186 .AppendASCII(test_name); |
| 187 } |
| 188 |
| 189 const Extension* GetInstalledExtension(const std::string& id) { |
| 190 return ExtensionRegistry::Get(profile()) |
| 191 ->GetExtensionById(id, ExtensionRegistry::EVERYTHING); |
| 192 } |
| 193 |
| 194 void SetCrxFilename(const std::string& filename) { |
| 195 GURL crx_url = GenerateTestServerUrl(kWebstoreDomain, filename); |
| 196 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| 197 switches::kAppsGalleryUpdateURL, crx_url.spec()); |
| 198 } |
| 199 |
| 200 void StartLauncherAndCheckResult( |
| 201 EphemeralAppLauncherForTest* launcher, |
| 202 EphemeralAppLauncher::LaunchResult expected_result, |
| 203 bool expect_install_initiated) { |
| 204 ExtensionTestMessageListener launched_listener("launched", false); |
| 205 LaunchObserver launch_observer; |
| 206 |
| 207 launcher->launch_callback_ = base::Bind(&LaunchObserver::OnLaunchCallback, |
| 208 base::Unretained(&launch_observer)); |
| 209 launcher->Start(); |
| 210 launch_observer.Wait(); |
| 211 |
| 212 // Verify the launch result. |
| 213 EXPECT_EQ(expected_result, launch_observer.result()); |
| 214 EXPECT_EQ(expect_install_initiated, launcher->install_initiated()); |
| 215 |
| 216 // Verify that the app was actually launched if the launcher succeeded. |
| 217 if (launch_observer.result() == EphemeralAppLauncher::LAUNCH_SUCCESS) |
| 218 EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); |
| 219 else |
| 220 EXPECT_FALSE(launched_listener.was_satisfied()); |
| 221 |
| 222 // Check the reference count to ensure the launcher instance will not be |
| 223 // leaked. |
| 224 EXPECT_TRUE(launcher->HasOneRef()); |
| 225 } |
| 226 |
| 227 void RunLaunchTest(const std::string& id, |
| 228 EphemeralAppLauncher::LaunchResult expected_result, |
| 229 bool expect_install_initiated) { |
| 230 scoped_refptr<EphemeralAppLauncherForTest> launcher( |
| 231 new EphemeralAppLauncherForTest(id, profile())); |
| 232 StartLauncherAndCheckResult( |
| 233 launcher.get(), expected_result, expect_install_initiated); |
| 234 } |
| 235 |
| 236 void ValidateAppInstalledEphemerally(const std::string& id) { |
| 237 EXPECT_TRUE(GetInstalledExtension(id)); |
| 238 EXPECT_TRUE(extensions::util::IsEphemeralApp(id, profile())); |
| 239 } |
| 240 |
| 241 const Extension* InstallAndDisableApp( |
| 242 const char* test_path, |
| 243 Extension::DisableReason disable_reason) { |
| 244 const Extension* app = InstallExtension(GetTestPath(test_path), 1); |
| 245 EXPECT_TRUE(app); |
| 246 if (!app) |
| 247 return NULL; |
| 248 |
| 249 if (disable_reason == Extension::DISABLE_GREYLIST) { |
| 250 ExtensionPrefs::Get(profile())->SetExtensionBlacklistState( |
| 251 app->id(), extensions::BLACKLISTED_MALWARE); |
| 252 } |
| 253 |
| 254 ExtensionService* service = |
| 255 ExtensionSystem::Get(profile())->extension_service(); |
| 256 service->DisableExtension(app->id(), disable_reason); |
| 257 |
| 258 if (disable_reason == Extension::DISABLE_PERMISSIONS_INCREASE) { |
| 259 // When an extension is disabled due to a permissions increase, this |
| 260 // flag needs to be set too, for some reason. |
| 261 ExtensionPrefs::Get(profile()) |
| 262 ->SetDidExtensionEscalatePermissions(app, true); |
| 263 } |
| 264 |
| 265 EXPECT_FALSE( |
| 266 ExtensionRegistry::Get(profile())->enabled_extensions().Contains( |
| 267 app->id())); |
| 268 return app; |
| 269 } |
| 270 }; |
| 271 |
| 272 class EphemeralAppLauncherTestDisabled : public EphemeralAppLauncherTest { |
| 273 public: |
| 274 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { |
| 275 // Skip EphemeralAppLauncherTest as it enables the feature. |
| 276 WebstoreInstallerTest::SetUpCommandLine(command_line); |
| 277 } |
| 278 }; |
| 279 |
| 280 // Verifies that an ephemeral app will not be installed and launched if the |
| 281 // feature is disabled. |
| 282 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTestDisabled, FeatureDisabled) { |
| 283 RunLaunchTest(kDefaultAppCrxFilename, |
| 284 EphemeralAppLauncher::LAUNCH_FEATURE_DISABLED, |
| 285 false); |
| 286 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
| 287 } |
| 288 |
| 289 // Verifies that an app with no permission warnings will be installed |
| 290 // ephemerally and launched without prompting the user. |
| 291 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
| 292 LaunchAppWithNoPermissionWarnings) { |
| 293 scoped_refptr<EphemeralAppLauncherForTest> launcher( |
| 294 new EphemeralAppLauncherForTest(kDefaultAppId, profile())); |
| 295 StartLauncherAndCheckResult( |
| 296 launcher.get(), EphemeralAppLauncher::LAUNCH_SUCCESS, true); |
| 297 ValidateAppInstalledEphemerally(kDefaultAppId); |
| 298 |
| 299 // Apps with no permission warnings should not result in a prompt. |
| 300 EXPECT_FALSE(launcher->install_prompt_created()); |
| 301 |
| 302 // After an app has been installed ephemerally, it can be launched again |
| 303 // without installing from the web store. |
| 304 RunLaunchTest(kDefaultAppId, EphemeralAppLauncher::LAUNCH_SUCCESS, false); |
| 305 } |
| 306 |
| 307 // Verifies that an app with permission warnings will be installed |
| 308 // ephemerally and launched if accepted by the user. |
| 309 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
| 310 LaunchAppWithPermissionsWarnings) { |
| 311 SetCrxFilename(kAppWithPermissionsFilename); |
| 312 AutoAcceptInstall(); |
| 313 |
| 314 scoped_refptr<EphemeralAppLauncherForTest> launcher( |
| 315 new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile())); |
| 316 StartLauncherAndCheckResult( |
| 317 launcher.get(), EphemeralAppLauncher::LAUNCH_SUCCESS, true); |
| 318 ValidateAppInstalledEphemerally(kAppWithPermissionsId); |
| 319 EXPECT_TRUE(launcher->install_prompt_created()); |
| 320 } |
| 321 |
| 322 // Verifies that an app with permission warnings will not be installed |
| 323 // ephemerally if cancelled by the user. |
| 324 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
| 325 CancelInstallAppWithPermissionWarnings) { |
| 326 SetCrxFilename(kAppWithPermissionsFilename); |
| 327 AutoCancelInstall(); |
| 328 |
| 329 scoped_refptr<EphemeralAppLauncherForTest> launcher( |
| 330 new EphemeralAppLauncherForTest(kAppWithPermissionsId, profile())); |
| 331 StartLauncherAndCheckResult( |
| 332 launcher.get(), EphemeralAppLauncher::LAUNCH_USER_CANCELLED, false); |
| 333 EXPECT_FALSE(GetInstalledExtension(kAppWithPermissionsId)); |
| 334 EXPECT_TRUE(launcher->install_prompt_created()); |
| 335 } |
| 336 |
| 337 // Verifies that an extension will not be installed ephemerally. |
| 338 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, InstallExtension) { |
| 339 RunLaunchTest(kExtensionId, |
| 340 EphemeralAppLauncher::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, |
| 341 false); |
| 342 EXPECT_FALSE(GetInstalledExtension(kExtensionId)); |
| 343 } |
| 344 |
| 345 // Verifies that an already installed extension will not be launched. |
| 346 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchExtension) { |
| 347 const Extension* extension = |
| 348 InstallExtension(GetTestPath(kExtensionTestPath), 1); |
| 349 ASSERT_TRUE(extension); |
| 350 RunLaunchTest(extension->id(), |
| 351 EphemeralAppLauncher::LAUNCH_UNSUPPORTED_EXTENSION_TYPE, |
| 352 false); |
| 353 } |
| 354 |
| 355 // Verifies that the EphemeralAppLauncher handles non-existent extension ids. |
| 356 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, NonExistentExtensionId) { |
| 357 RunLaunchTest( |
| 358 kNonExistentId, EphemeralAppLauncher::LAUNCH_INSTALL_ERROR, false); |
| 359 EXPECT_FALSE(GetInstalledExtension(kNonExistentId)); |
| 360 } |
| 361 |
| 362 // Verifies that an app blocked by management policy is not installed |
| 363 // ephemerally. |
| 364 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlockedByPolicy) { |
| 365 // Register a provider that blocks the installation of all apps. |
| 366 ManagementPolicyMock policy; |
| 367 ExtensionSystem::Get(profile())->management_policy()->RegisterProvider( |
| 368 &policy); |
| 369 |
| 370 RunLaunchTest( |
| 371 kDefaultAppId, EphemeralAppLauncher::LAUNCH_BLOCKED_BY_POLICY, false); |
| 372 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
| 373 } |
| 374 |
| 375 // Verifies that an app blacklisted for malware is not installed ephemerally. |
| 376 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistedForMalware) { |
| 377 // Mock a BLACKLISTED_MALWARE return status. |
| 378 extensions::TestBlacklist blacklist_tester( |
| 379 ExtensionSystem::Get(profile())->blacklist()); |
| 380 blacklist_tester.SetBlacklistState( |
| 381 kDefaultAppId, extensions::BLACKLISTED_MALWARE, false); |
| 382 |
| 383 RunLaunchTest(kDefaultAppId, EphemeralAppLauncher::LAUNCH_BLACKLISTED, false); |
| 384 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
| 385 } |
| 386 |
| 387 // Verifies that an app with unknown blacklist status is installed ephemerally |
| 388 // and launched. |
| 389 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, BlacklistStateUnknown) { |
| 390 // Mock a BLACKLISTED_MALWARE return status. |
| 391 extensions::TestBlacklist blacklist_tester( |
| 392 ExtensionSystem::Get(profile())->blacklist()); |
| 393 blacklist_tester.SetBlacklistState( |
| 394 kDefaultAppId, extensions::BLACKLISTED_UNKNOWN, false); |
| 395 |
| 396 RunLaunchTest(kDefaultAppId, EphemeralAppLauncher::LAUNCH_SUCCESS, true); |
| 397 ValidateAppInstalledEphemerally(kDefaultAppId); |
| 398 } |
| 399 |
| 400 // Verifies that an app with unsupported requirements is not installed |
| 401 // ephemerally. |
| 402 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, UnsupportedRequirements) { |
| 403 scoped_refptr<EphemeralAppLauncherForTest> launcher( |
| 404 new EphemeralAppLauncherForTest(kDefaultAppId, profile())); |
| 405 launcher->set_requirements_error("App has unsupported requirements"); |
| 406 |
| 407 StartLauncherAndCheckResult( |
| 408 launcher.get(), EphemeralAppLauncher::LAUNCH_MISSING_DEPENDENCIES, false); |
| 409 EXPECT_FALSE(GetInstalledExtension(kDefaultAppId)); |
| 410 } |
| 411 |
| 412 // Verifies that an app disabled due to permissions increase can be enabled |
| 413 // and launched. |
| 414 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableAndLaunchApp) { |
| 415 const Extension* app = InstallAndDisableApp( |
| 416 kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE); |
| 417 ASSERT_TRUE(app); |
| 418 |
| 419 AutoAcceptInstall(); |
| 420 RunLaunchTest(app->id(), EphemeralAppLauncher::LAUNCH_SUCCESS, false); |
| 421 } |
| 422 |
| 423 // Verifies that if the user cancels the enable flow, the app will not be |
| 424 // enabled and launched. |
| 425 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, EnableCancelled) { |
| 426 const Extension* app = InstallAndDisableApp( |
| 427 kDefaultAppTestPath, Extension::DISABLE_PERMISSIONS_INCREASE); |
| 428 ASSERT_TRUE(app); |
| 429 |
| 430 AutoCancelInstall(); |
| 431 RunLaunchTest(app->id(), EphemeralAppLauncher::LAUNCH_USER_CANCELLED, false); |
| 432 } |
| 433 |
| 434 // Verifies that an installed app that had been blocked by policy cannot be |
| 435 // launched. |
| 436 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchAppBlockedByPolicy) { |
| 437 const Extension* app = InstallExtension(GetTestPath(kDefaultAppTestPath), 1); |
| 438 ASSERT_TRUE(app); |
| 439 |
| 440 // Simulate blocking of the app after it has been installed. |
| 441 ManagementPolicyMock policy; |
| 442 ExtensionSystem::Get(profile())->management_policy()->RegisterProvider( |
| 443 &policy); |
| 444 ExtensionSystem::Get(profile())->extension_service()->CheckManagementPolicy(); |
| 445 |
| 446 RunLaunchTest( |
| 447 app->id(), EphemeralAppLauncher::LAUNCH_BLOCKED_BY_POLICY, false); |
| 448 } |
| 449 |
| 450 // Verifies that an installed blacklisted app cannot be launched. |
| 451 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, LaunchBlacklistedApp) { |
| 452 const Extension* app = |
| 453 InstallAndDisableApp(kDefaultAppTestPath, Extension::DISABLE_GREYLIST); |
| 454 ASSERT_TRUE(app); |
| 455 |
| 456 RunLaunchTest(app->id(), EphemeralAppLauncher::LAUNCH_BLACKLISTED, false); |
| 457 } |
| 458 |
| 459 // Verifies that an installed app with unsupported requirements cannot be |
| 460 // launched. |
| 461 IN_PROC_BROWSER_TEST_F(EphemeralAppLauncherTest, |
| 462 LaunchAppWithUnsupportedRequirements) { |
| 463 const Extension* app = InstallAndDisableApp( |
| 464 kDefaultAppTestPath, Extension::DISABLE_UNSUPPORTED_REQUIREMENT); |
| 465 ASSERT_TRUE(app); |
| 466 |
| 467 RunLaunchTest( |
| 468 app->id(), EphemeralAppLauncher::LAUNCH_MISSING_DEPENDENCIES, false); |
| 469 } |
OLD | NEW |