OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "chrome/browser/extensions/extension_service_test_with_install.h" |
| 6 |
| 7 #include "base/files/file_util.h" |
| 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "chrome/browser/extensions/crx_installer.h" |
| 10 #include "chrome/browser/extensions/extension_creator.h" |
| 11 #include "chrome/browser/extensions/extension_error_reporter.h" |
| 12 #include "chrome/browser/profiles/profile.h" |
| 13 #include "content/public/browser/notification_service.h" |
| 14 #include "extensions/browser/extension_registry.h" |
| 15 #include "extensions/browser/notification_types.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" |
| 17 |
| 18 namespace extensions { |
| 19 |
| 20 namespace { |
| 21 |
| 22 struct ExtensionsOrder { |
| 23 bool operator()(const scoped_refptr<const Extension>& a, |
| 24 const scoped_refptr<const Extension>& b) { |
| 25 return a->name() < b->name(); |
| 26 } |
| 27 }; |
| 28 |
| 29 // Helper method to set up a WindowedNotificationObserver to wait for a |
| 30 // specific CrxInstaller to finish if we don't know the value of the |
| 31 // |installer| yet. |
| 32 bool IsCrxInstallerDone(extensions::CrxInstaller** installer, |
| 33 const content::NotificationSource& source, |
| 34 const content::NotificationDetails& details) { |
| 35 return content::Source<extensions::CrxInstaller>(source).ptr() == *installer; |
| 36 } |
| 37 |
| 38 } // namespace |
| 39 |
| 40 ExtensionServiceTestWithInstall::ExtensionServiceTestWithInstall() |
| 41 : installed_(nullptr), |
| 42 was_update_(false), |
| 43 unloaded_reason_(UnloadedExtensionInfo::REASON_UNDEFINED), |
| 44 expected_extensions_count_(0){ |
| 45 // TODO(treib,devlin): This should use ExtensionRegistryObserver instead. |
| 46 registrar_.Add(this, |
| 47 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, |
| 48 content::NotificationService::AllSources()); |
| 49 registrar_.Add(this, |
| 50 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, |
| 51 content::NotificationService::AllSources()); |
| 52 registrar_.Add( |
| 53 this, |
| 54 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, |
| 55 content::NotificationService::AllSources()); |
| 56 } |
| 57 |
| 58 ExtensionServiceTestWithInstall::~ExtensionServiceTestWithInstall() {} |
| 59 |
| 60 // static |
| 61 std::vector<base::string16> ExtensionServiceTestWithInstall::GetErrors() { |
| 62 const std::vector<base::string16>* errors = |
| 63 ExtensionErrorReporter::GetInstance()->GetErrors(); |
| 64 std::vector<base::string16> ret_val; |
| 65 |
| 66 for (const base::string16& error : *errors) { |
| 67 std::string utf8_error = base::UTF16ToUTF8(error); |
| 68 if (utf8_error.find(".svn") == std::string::npos) { |
| 69 ret_val.push_back(error); |
| 70 } |
| 71 } |
| 72 |
| 73 // The tests rely on the errors being in a certain order, which can vary |
| 74 // depending on how filesystem iteration works. |
| 75 std::stable_sort(ret_val.begin(), ret_val.end()); |
| 76 |
| 77 return ret_val; |
| 78 } |
| 79 |
| 80 void ExtensionServiceTestWithInstall::PackCRX(const base::FilePath& dir_path, |
| 81 const base::FilePath& pem_path, |
| 82 const base::FilePath& crx_path) { |
| 83 // Use the existing pem key, if provided. |
| 84 base::FilePath pem_output_path; |
| 85 if (pem_path.value().empty()) { |
| 86 pem_output_path = crx_path.DirName().AppendASCII("temp.pem"); |
| 87 } else { |
| 88 ASSERT_TRUE(base::PathExists(pem_path)); |
| 89 } |
| 90 |
| 91 ASSERT_TRUE(base::DeleteFile(crx_path, false)); |
| 92 |
| 93 scoped_ptr<ExtensionCreator> creator(new ExtensionCreator()); |
| 94 ASSERT_TRUE(creator->Run(dir_path, |
| 95 crx_path, |
| 96 pem_path, |
| 97 pem_output_path, |
| 98 ExtensionCreator::kOverwriteCRX)); |
| 99 |
| 100 ASSERT_TRUE(base::PathExists(crx_path)); |
| 101 } |
| 102 |
| 103 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
| 104 const base::FilePath& dir_path, |
| 105 const base::FilePath& pem_path, |
| 106 InstallState install_state, |
| 107 int creation_flags) { |
| 108 base::FilePath crx_path; |
| 109 base::ScopedTempDir temp_dir; |
| 110 EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 111 crx_path = temp_dir.path().AppendASCII("temp.crx"); |
| 112 |
| 113 PackCRX(dir_path, pem_path, crx_path); |
| 114 return InstallCRX(crx_path, install_state, creation_flags); |
| 115 } |
| 116 |
| 117 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
| 118 const base::FilePath& dir_path, |
| 119 const base::FilePath& pem_path, |
| 120 InstallState install_state) { |
| 121 return PackAndInstallCRX(dir_path, pem_path, install_state, |
| 122 Extension::NO_FLAGS); |
| 123 } |
| 124 |
| 125 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
| 126 const base::FilePath& dir_path, |
| 127 InstallState install_state) { |
| 128 return PackAndInstallCRX(dir_path, base::FilePath(), install_state, |
| 129 Extension::NO_FLAGS); |
| 130 } |
| 131 |
| 132 // Attempts to install an extension. Use INSTALL_FAILED if the installation |
| 133 // is expected to fail. |
| 134 // If |install_state| is INSTALL_UPDATED, and |expected_old_name| is |
| 135 // non-empty, expects that the existing extension's title was |
| 136 // |expected_old_name|. |
| 137 const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
| 138 const base::FilePath& path, |
| 139 InstallState install_state, |
| 140 int creation_flags, |
| 141 const std::string& expected_old_name) { |
| 142 InstallCRXInternal(path, creation_flags); |
| 143 return VerifyCrxInstall(path, install_state, expected_old_name); |
| 144 } |
| 145 |
| 146 // Attempts to install an extension. Use INSTALL_FAILED if the installation |
| 147 // is expected to fail. |
| 148 const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
| 149 const base::FilePath& path, |
| 150 InstallState install_state, |
| 151 int creation_flags) { |
| 152 return InstallCRX(path, install_state, creation_flags, std::string()); |
| 153 } |
| 154 |
| 155 // Attempts to install an extension. Use INSTALL_FAILED if the installation |
| 156 // is expected to fail. |
| 157 const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
| 158 const base::FilePath& path, |
| 159 InstallState install_state) { |
| 160 return InstallCRX(path, install_state, Extension::NO_FLAGS); |
| 161 } |
| 162 |
| 163 const Extension* ExtensionServiceTestWithInstall::InstallCRXFromWebStore( |
| 164 const base::FilePath& path, |
| 165 InstallState install_state) { |
| 166 InstallCRXInternal(path, Extension::FROM_WEBSTORE); |
| 167 return VerifyCrxInstall(path, install_state); |
| 168 } |
| 169 |
| 170 const Extension* ExtensionServiceTestWithInstall::InstallCRXWithLocation( |
| 171 const base::FilePath& crx_path, |
| 172 Manifest::Location install_location, |
| 173 InstallState install_state) { |
| 174 EXPECT_TRUE(base::PathExists(crx_path)) |
| 175 << "Path does not exist: "<< crx_path.value().c_str(); |
| 176 // no client (silent install) |
| 177 scoped_refptr<CrxInstaller> installer( |
| 178 CrxInstaller::CreateSilent(service())); |
| 179 installer->set_install_source(install_location); |
| 180 |
| 181 content::WindowedNotificationObserver observer( |
| 182 extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| 183 content::NotificationService::AllSources()); |
| 184 installer->InstallCrx(crx_path); |
| 185 observer.Wait(); |
| 186 |
| 187 return VerifyCrxInstall(crx_path, install_state); |
| 188 } |
| 189 |
| 190 const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( |
| 191 const base::FilePath& path, |
| 192 InstallState install_state) { |
| 193 return VerifyCrxInstall(path, install_state, std::string()); |
| 194 } |
| 195 |
| 196 const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( |
| 197 const base::FilePath& path, |
| 198 InstallState install_state, |
| 199 const std::string& expected_old_name) { |
| 200 std::vector<base::string16> errors = GetErrors(); |
| 201 const Extension* extension = NULL; |
| 202 if (install_state != INSTALL_FAILED) { |
| 203 if (install_state == INSTALL_NEW) |
| 204 ++expected_extensions_count_; |
| 205 |
| 206 EXPECT_TRUE(installed_) << path.value(); |
| 207 // If and only if INSTALL_UPDATED, it should have the is_update flag. |
| 208 EXPECT_EQ(install_state == INSTALL_UPDATED, was_update_) |
| 209 << path.value(); |
| 210 // If INSTALL_UPDATED, old_name_ should match the given string. |
| 211 if (install_state == INSTALL_UPDATED && !expected_old_name.empty()) |
| 212 EXPECT_EQ(expected_old_name, old_name_); |
| 213 EXPECT_EQ(0u, errors.size()) << path.value(); |
| 214 |
| 215 if (install_state == INSTALL_WITHOUT_LOAD) { |
| 216 EXPECT_EQ(0u, loaded_.size()) << path.value(); |
| 217 } else { |
| 218 EXPECT_EQ(1u, loaded_.size()) << path.value(); |
| 219 size_t actual_extension_count = |
| 220 registry()->enabled_extensions().size() + |
| 221 registry()->disabled_extensions().size(); |
| 222 EXPECT_EQ(expected_extensions_count_, actual_extension_count) << |
| 223 path.value(); |
| 224 extension = loaded_[0].get(); |
| 225 EXPECT_TRUE(service()->GetExtensionById(extension->id(), false)) |
| 226 << path.value(); |
| 227 } |
| 228 |
| 229 for (std::vector<base::string16>::iterator err = errors.begin(); |
| 230 err != errors.end(); ++err) { |
| 231 LOG(ERROR) << *err; |
| 232 } |
| 233 } else { |
| 234 EXPECT_FALSE(installed_) << path.value(); |
| 235 EXPECT_EQ(0u, loaded_.size()) << path.value(); |
| 236 EXPECT_EQ(1u, errors.size()) << path.value(); |
| 237 } |
| 238 |
| 239 installed_ = NULL; |
| 240 was_update_ = false; |
| 241 old_name_ = ""; |
| 242 loaded_.clear(); |
| 243 ExtensionErrorReporter::GetInstance()->ClearErrors(); |
| 244 return extension; |
| 245 } |
| 246 |
| 247 void ExtensionServiceTestWithInstall::PackCRXAndUpdateExtension( |
| 248 const std::string& id, |
| 249 const base::FilePath& dir_path, |
| 250 const base::FilePath& pem_path, |
| 251 UpdateState expected_state) { |
| 252 base::ScopedTempDir temp_dir; |
| 253 EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 254 base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); |
| 255 |
| 256 PackCRX(dir_path, pem_path, crx_path); |
| 257 UpdateExtension(id, crx_path, expected_state); |
| 258 } |
| 259 |
| 260 void ExtensionServiceTestWithInstall::UpdateExtension( |
| 261 const std::string& id, |
| 262 const base::FilePath& in_path, |
| 263 UpdateState expected_state) { |
| 264 ASSERT_TRUE(base::PathExists(in_path)); |
| 265 |
| 266 // We need to copy this to a temporary location because Update() will delete |
| 267 // it. |
| 268 base::FilePath path = temp_dir().path(); |
| 269 path = path.Append(in_path.BaseName()); |
| 270 ASSERT_TRUE(base::CopyFile(in_path, path)); |
| 271 |
| 272 int previous_enabled_extension_count = |
| 273 registry()->enabled_extensions().size(); |
| 274 int previous_installed_extension_count = |
| 275 previous_enabled_extension_count + |
| 276 registry()->disabled_extensions().size(); |
| 277 |
| 278 extensions::CrxInstaller* installer = NULL; |
| 279 content::WindowedNotificationObserver observer( |
| 280 extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| 281 base::Bind(&IsCrxInstallerDone, &installer)); |
| 282 service()->UpdateExtension(extensions::CRXFileInfo(id, path), true, |
| 283 &installer); |
| 284 |
| 285 if (installer) |
| 286 observer.Wait(); |
| 287 else |
| 288 base::RunLoop().RunUntilIdle(); |
| 289 |
| 290 std::vector<base::string16> errors = GetErrors(); |
| 291 int error_count = errors.size(); |
| 292 int enabled_extension_count = registry()->enabled_extensions().size(); |
| 293 int installed_extension_count = |
| 294 enabled_extension_count + registry()->disabled_extensions().size(); |
| 295 |
| 296 int expected_error_count = (expected_state == FAILED) ? 1 : 0; |
| 297 EXPECT_EQ(expected_error_count, error_count) << path.value(); |
| 298 |
| 299 if (expected_state <= FAILED) { |
| 300 EXPECT_EQ(previous_enabled_extension_count, |
| 301 enabled_extension_count); |
| 302 EXPECT_EQ(previous_installed_extension_count, |
| 303 installed_extension_count); |
| 304 } else { |
| 305 int expected_installed_extension_count = |
| 306 (expected_state >= INSTALLED) ? 1 : 0; |
| 307 int expected_enabled_extension_count = |
| 308 (expected_state >= ENABLED) ? 1 : 0; |
| 309 EXPECT_EQ(expected_installed_extension_count, |
| 310 installed_extension_count); |
| 311 EXPECT_EQ(expected_enabled_extension_count, |
| 312 enabled_extension_count); |
| 313 } |
| 314 |
| 315 // Update() should the temporary input file. |
| 316 EXPECT_FALSE(base::PathExists(path)); |
| 317 } |
| 318 |
| 319 void ExtensionServiceTestWithInstall::UninstallExtension(const std::string& id, |
| 320 bool use_helper) { |
| 321 UninstallExtension(id, use_helper, Extension::ENABLED); |
| 322 } |
| 323 |
| 324 void ExtensionServiceTestWithInstall::UninstallExtension( |
| 325 const std::string& id, |
| 326 bool use_helper, |
| 327 Extension::State expected_state) { |
| 328 // Verify that the extension is installed. |
| 329 base::FilePath extension_path = extensions_install_dir().AppendASCII(id); |
| 330 EXPECT_TRUE(base::PathExists(extension_path)); |
| 331 size_t pref_key_count = GetPrefKeyCount(); |
| 332 EXPECT_GT(pref_key_count, 0u); |
| 333 ValidateIntegerPref(id, "state", expected_state); |
| 334 |
| 335 // Uninstall it. |
| 336 if (use_helper) { |
| 337 EXPECT_TRUE(ExtensionService::UninstallExtensionHelper( |
| 338 service(), id, extensions::UNINSTALL_REASON_FOR_TESTING)); |
| 339 } else { |
| 340 EXPECT_TRUE(service()->UninstallExtension( |
| 341 id, |
| 342 extensions::UNINSTALL_REASON_FOR_TESTING, |
| 343 base::Bind(&base::DoNothing), |
| 344 NULL)); |
| 345 } |
| 346 --expected_extensions_count_; |
| 347 |
| 348 // We should get an unload notification. |
| 349 EXPECT_FALSE(unloaded_id_.empty()); |
| 350 EXPECT_EQ(id, unloaded_id_); |
| 351 |
| 352 // Verify uninstalled state. |
| 353 size_t new_pref_key_count = GetPrefKeyCount(); |
| 354 if (new_pref_key_count == pref_key_count) { |
| 355 ValidateIntegerPref(id, "state", |
| 356 Extension::EXTERNAL_EXTENSION_UNINSTALLED); |
| 357 } else { |
| 358 EXPECT_EQ(new_pref_key_count, pref_key_count - 1); |
| 359 } |
| 360 |
| 361 // The extension should not be in the service anymore. |
| 362 EXPECT_FALSE(service()->GetInstalledExtension(id)); |
| 363 base::RunLoop().RunUntilIdle(); |
| 364 |
| 365 // The directory should be gone. |
| 366 EXPECT_FALSE(base::PathExists(extension_path)); |
| 367 } |
| 368 |
| 369 void ExtensionServiceTestWithInstall::TerminateExtension( |
| 370 const std::string& id) { |
| 371 const Extension* extension = service()->GetInstalledExtension(id); |
| 372 if (!extension) { |
| 373 ADD_FAILURE(); |
| 374 return; |
| 375 } |
| 376 service()->TrackTerminatedExtensionForTest(extension); |
| 377 } |
| 378 |
| 379 void ExtensionServiceTestWithInstall::Observe( |
| 380 int type, |
| 381 const content::NotificationSource& source, |
| 382 const content::NotificationDetails& details) { |
| 383 switch (type) { |
| 384 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { |
| 385 const Extension* extension = |
| 386 content::Details<const Extension>(details).ptr(); |
| 387 loaded_.push_back(make_scoped_refptr(extension)); |
| 388 // The tests rely on the errors being in a certain order, which can vary |
| 389 // depending on how filesystem iteration works. |
| 390 std::stable_sort(loaded_.begin(), loaded_.end(), ExtensionsOrder()); |
| 391 break; |
| 392 } |
| 393 |
| 394 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { |
| 395 UnloadedExtensionInfo* unloaded_info = |
| 396 content::Details<UnloadedExtensionInfo>(details).ptr(); |
| 397 const Extension* e = unloaded_info->extension; |
| 398 unloaded_id_ = e->id(); |
| 399 unloaded_reason_ = unloaded_info->reason; |
| 400 extensions::ExtensionList::iterator i = |
| 401 std::find(loaded_.begin(), loaded_.end(), e); |
| 402 // TODO(erikkay) fix so this can be an assert. Right now the tests |
| 403 // are manually calling clear() on loaded_, so this isn't doable. |
| 404 if (i == loaded_.end()) |
| 405 return; |
| 406 loaded_.erase(i); |
| 407 break; |
| 408 } |
| 409 case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { |
| 410 const extensions::InstalledExtensionInfo* installed_info = |
| 411 content::Details<const extensions::InstalledExtensionInfo>(details) |
| 412 .ptr(); |
| 413 installed_ = installed_info->extension; |
| 414 was_update_ = installed_info->is_update; |
| 415 old_name_ = installed_info->old_name; |
| 416 break; |
| 417 } |
| 418 |
| 419 default: |
| 420 DCHECK(false); |
| 421 } |
| 422 } |
| 423 |
| 424 // Create a CrxInstaller and install the CRX file. |
| 425 // Instead of calling this method yourself, use InstallCRX(), which does extra |
| 426 // error checking. |
| 427 void ExtensionServiceTestWithInstall::InstallCRXInternal( |
| 428 const base::FilePath& crx_path, |
| 429 int creation_flags) { |
| 430 ASSERT_TRUE(base::PathExists(crx_path)) |
| 431 << "Path does not exist: "<< crx_path.value().c_str(); |
| 432 scoped_refptr<CrxInstaller> installer( |
| 433 CrxInstaller::CreateSilent(service())); |
| 434 installer->set_creation_flags(creation_flags); |
| 435 if (!(creation_flags & Extension::WAS_INSTALLED_BY_DEFAULT)) |
| 436 installer->set_allow_silent_install(true); |
| 437 |
| 438 content::WindowedNotificationObserver observer( |
| 439 extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| 440 content::Source<extensions::CrxInstaller>(installer.get())); |
| 441 |
| 442 installer->InstallCrx(crx_path); |
| 443 |
| 444 observer.Wait(); |
| 445 } |
| 446 |
| 447 } // namespace extensions |
OLD | NEW |