1 // Copyright (c) 2012 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/sync/test/integration/sync_extension_helper.h" 6 7 #include "base/files/file_path.h" 8 #include "base/files/file_util.h" 9 #include "base/logging.h" 10 #include "base/values.h" 11 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_util.h" 13 #include "chrome/browser/extensions/pending_extension_info.h" 14 #include "chrome/browser/extensions/pending_extension_manager.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 17 #include "chrome/browser/sync/test/integration/sync_test.h" 18 #include "components/crx_file/id_util.h" 19 #include "extensions/browser/extension_registry.h" 20 #include "extensions/browser/extension_system.h" 21 #include "extensions/browser/install_flag.h" 22 #include "extensions/browser/uninstall_reason.h" 23 #include "extensions/common/extension.h" 24 #include "extensions/common/extension_set.h" 25 #include "extensions/common/manifest_constants.h" 26 #include "sync/api/string_ordinal.h" 27 #include "testing/gtest/include/gtest/gtest.h" 28 29 using extensions::Extension; 30 using extensions::ExtensionRegistry; 31 using extensions::Manifest; 32 33 SyncExtensionHelper::ExtensionState::ExtensionState() 34 : enabled_state(ENABLED), incognito_enabled(false) {} 35 36 SyncExtensionHelper::ExtensionState::~ExtensionState() {} 37 38 bool SyncExtensionHelper::ExtensionState::Equals( 39 const SyncExtensionHelper::ExtensionState &other) const { 40 return ((enabled_state == other.enabled_state) && 41 (incognito_enabled == other.incognito_enabled)); 42 } 43 44 // static 45 SyncExtensionHelper* SyncExtensionHelper::GetInstance() { 46 SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get(); 47 instance->SetupIfNecessary(sync_datatype_helper::test()); 48 return instance; 49 } 50 51 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {} 52 53 SyncExtensionHelper::~SyncExtensionHelper() {} 54 55 void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) { 56 if (setup_completed_) 57 return; 58 59 for (int i = 0; i < test->num_clients(); ++i) { 60 SetupProfile(test->GetProfile(i)); 61 } 62 SetupProfile(test->verifier()); 63 64 setup_completed_ = true; 65 } 66 67 std::string SyncExtensionHelper::InstallExtension( 68 Profile* profile, const std::string& name, Manifest::Type type) { 69 scoped_refptr<Extension> extension = GetExtension(profile, name, type); 70 if (!extension.get()) { 71 NOTREACHED() << "Could not install extension " << name; 72 return std::string(); 73 } 74 extensions::ExtensionSystem::Get(profile) 75 ->extension_service() 76 ->OnExtensionInstalled(extension.get(), 77 syncer::StringOrdinal(), 78 extensions::kInstallFlagInstallImmediately); 79 return extension->id(); 80 } 81 82 void SyncExtensionHelper::UninstallExtension( 83 Profile* profile, const std::string& name) { 84 ExtensionService::UninstallExtensionHelper( 85 extensions::ExtensionSystem::Get(profile)->extension_service(), 86 crx_file::id_util::GenerateId(name), 87 extensions::UNINSTALL_REASON_SYNC); 88 } 89 90 std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames( 91 Profile* profile) const { 92 std::vector<std::string> names; 93 94 scoped_ptr<const extensions::ExtensionSet> extensions( 95 extensions::ExtensionRegistry::Get(profile) 96 ->GenerateInstalledExtensionsSet()); 97 for (extensions::ExtensionSet::const_iterator it = extensions->begin(); 98 it != extensions->end(); ++it) { 99 names.push_back((*it)->name()); 100 } 101 102 return names; 103 } 104 105 void SyncExtensionHelper::EnableExtension(Profile* profile, 106 const std::string& name) { 107 extensions::ExtensionSystem::Get(profile) 108 ->extension_service() 109 ->EnableExtension(crx_file::id_util::GenerateId(name)); 110 } 111 112 void SyncExtensionHelper::DisableExtension(Profile* profile, 113 const std::string& name) { 114 extensions::ExtensionSystem::Get(profile) 115 ->extension_service() 116 ->DisableExtension(crx_file::id_util::GenerateId(name), 117 Extension::DISABLE_USER_ACTION); 118 } 119 120 bool SyncExtensionHelper::IsExtensionEnabled( 121 Profile* profile, const std::string& name) const { 122 return extensions::ExtensionSystem::Get(profile) 123 ->extension_service() 124 ->IsExtensionEnabled(crx_file::id_util::GenerateId(name)); 125 } 126 127 void SyncExtensionHelper::IncognitoEnableExtension( 128 Profile* profile, const std::string& name) { 129 extensions::util::SetIsIncognitoEnabled( 130 crx_file::id_util::GenerateId(name), profile, true); 131 } 132 133 void SyncExtensionHelper::IncognitoDisableExtension( 134 Profile* profile, const std::string& name) { 135 extensions::util::SetIsIncognitoEnabled( 136 crx_file::id_util::GenerateId(name), profile, false); 137 } 138 139 bool SyncExtensionHelper::IsIncognitoEnabled( 140 Profile* profile, const std::string& name) const { 141 return extensions::util::IsIncognitoEnabled( 142 crx_file::id_util::GenerateId(name), profile); 143 } 144 145 146 bool SyncExtensionHelper::IsExtensionPendingInstallForSync( 147 Profile* profile, const std::string& id) const { 148 const extensions::PendingExtensionManager* pending_extension_manager = 149 extensions::ExtensionSystem::Get(profile) 150 ->extension_service() 151 ->pending_extension_manager(); 152 const extensions::PendingExtensionInfo* info = 153 pending_extension_manager->GetById(id); 154 if (!info) 155 return false; 156 return info->is_from_sync(); 157 } 158 159 void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile* profile) { 160 // TODO(akalin): Mock out the servers that the extensions auto-update 161 // mechanism talk to so as to more closely match what actually happens. 162 // Background networking will need to be re-enabled for extensions tests. 163 164 // We make a copy here since InstallExtension() removes the 165 // extension from the extensions service's copy. 166 const extensions::PendingExtensionManager* pending_extension_manager = 167 extensions::ExtensionSystem::Get(profile) 168 ->extension_service() 169 ->pending_extension_manager(); 170 171 std::list<std::string> pending_crx_ids; 172 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids); 173 174 std::list<std::string>::const_iterator iter; 175 const extensions::PendingExtensionInfo* info = NULL; 176 for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) { 177 ASSERT_TRUE((info = pending_extension_manager->GetById(*iter))); 178 if (!info->is_from_sync()) 179 continue; 180 181 StringMap::const_iterator iter2 = id_to_name_.find(*iter); 182 if (iter2 == id_to_name_.end()) { 183 ADD_FAILURE() << "Could not get name for id " << *iter 184 << " (profile = " << profile->GetDebugName() << ")"; 185 continue; 186 } 187 TypeMap::const_iterator iter3 = id_to_type_.find(*iter); 188 if (iter3 == id_to_type_.end()) { 189 ADD_FAILURE() << "Could not get type for id " << *iter 190 << " (profile = " << profile->GetDebugName() << ")"; 191 } 192 InstallExtension(profile, iter2->second, iter3->second); 193 } 194 } 195 196 SyncExtensionHelper::ExtensionStateMap 197 SyncExtensionHelper::GetExtensionStates(Profile* profile) { 198 const std::string& profile_debug_name = profile->GetDebugName(); 199 200 ExtensionStateMap extension_state_map; 201 202 scoped_ptr<const extensions::ExtensionSet> extensions( 203 extensions::ExtensionRegistry::Get(profile) 204 ->GenerateInstalledExtensionsSet()); 205 206 ExtensionService* extension_service = 207 extensions::ExtensionSystem::Get(profile)->extension_service(); 208 for (extensions::ExtensionSet::const_iterator it = extensions->begin(); 209 it != extensions->end(); ++it) { 210 const std::string& id = (*it)->id(); 211 extension_state_map[id].enabled_state = 212 extension_service->IsExtensionEnabled(id) ? 213 ExtensionState::ENABLED : 214 ExtensionState::DISABLED; 215 extension_state_map[id].incognito_enabled = 216 extensions::util::IsIncognitoEnabled(id, profile); 217 218 DVLOG(2) << "Extension " << (*it)->id() << " in profile " 219 << profile_debug_name << " is " 220 << (extension_service->IsExtensionEnabled(id) ? 221 "enabled" : "disabled"); 222 } 223 224 const extensions::PendingExtensionManager* pending_extension_manager = 225 extension_service->pending_extension_manager(); 226 227 std::list<std::string> pending_crx_ids; 228 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids); 229 230 std::list<std::string>::const_iterator id; 231 for (id = pending_crx_ids.begin(); id != pending_crx_ids.end(); ++id) { 232 extension_state_map[*id].enabled_state = ExtensionState::PENDING; 233 extension_state_map[*id].incognito_enabled = 234 extensions::util::IsIncognitoEnabled(*id, profile); 235 DVLOG(2) << "Extension " << *id << " in profile " 236 << profile_debug_name << " is pending"; 237 } 238 239 return extension_state_map; 240 } 241 242 bool SyncExtensionHelper::ExtensionStatesMatch( 243 Profile* profile1, Profile* profile2) { 244 const ExtensionStateMap& state_map1 = GetExtensionStates(profile1); 245 const ExtensionStateMap& state_map2 = GetExtensionStates(profile2); 246 if (state_map1.size() != state_map2.size()) { 247 DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName() 248 << " does not match profile " << profile2->GetDebugName(); 249 return false; 250 } 251 252 ExtensionStateMap::const_iterator it1 = state_map1.begin(); 253 ExtensionStateMap::const_iterator it2 = state_map2.begin(); 254 while (it1 != state_map1.end()) { 255 if (it1->first != it2->first) { 256 DVLOG(1) << "Extensions for profile " << profile1->GetDebugName() 257 << " do not match profile " << profile2->GetDebugName(); 258 return false; 259 } else if (!it1->second.Equals(it2->second)) { 260 DVLOG(1) << "Extension states for profile " << profile1->GetDebugName() 261 << " do not match profile " << profile2->GetDebugName(); 262 return false; 263 } 264 ++it1; 265 ++it2; 266 } 267 return true; 268 } 269 270 void SyncExtensionHelper::SetupProfile(Profile* profile) { 271 extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true); 272 profile_extensions_.insert(make_pair(profile, ExtensionNameMap())); 273 } 274 275 namespace { 276 277 std::string NameToPublicKey(const std::string& name) { 278 std::string public_key; 279 std::string pem; 280 EXPECT_TRUE(Extension::ProducePEM(name, &pem) && 281 Extension::FormatPEMForFileOutput(pem, &public_key, 282 true /* is_public */)); 283 return public_key; 284 } 285 286 // TODO(akalin): Somehow unify this with MakeExtension() in 287 // extension_util_unittest.cc. 288 scoped_refptr<Extension> CreateExtension(const base::FilePath& base_dir, 289 const std::string& name, 290 Manifest::Type type) { 291 base::DictionaryValue source; 292 source.SetString(extensions::manifest_keys::kName, name); 293 const std::string& public_key = NameToPublicKey(name); 294 source.SetString(extensions::manifest_keys::kPublicKey, public_key); 295 source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0"); 296 switch (type) { 297 case Manifest::TYPE_EXTENSION: 298 // Do nothing. 299 break; 300 case Manifest::TYPE_THEME: 301 source.Set(extensions::manifest_keys::kTheme, 302 new base::DictionaryValue()); 303 break; 304 case Manifest::TYPE_HOSTED_APP: 305 case Manifest::TYPE_LEGACY_PACKAGED_APP: 306 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue()); 307 source.SetString(extensions::manifest_keys::kLaunchWebURL, 308 "http://www.example.com"); 309 break; 310 case Manifest::TYPE_PLATFORM_APP: { 311 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue()); 312 source.Set(extensions::manifest_keys::kPlatformAppBackground, 313 new base::DictionaryValue()); 314 base::ListValue* scripts = new base::ListValue(); 315 scripts->AppendString("main.js"); 316 source.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts, 317 scripts); 318 break; 319 } 320 default: 321 ADD_FAILURE(); 322 return NULL; 323 } 324 const base::FilePath sub_dir = base::FilePath().AppendASCII(name); 325 base::FilePath extension_dir; 326 if (!base::PathExists(base_dir) && 327 !base::CreateDirectory(base_dir)) { 328 ADD_FAILURE(); 329 return NULL; 330 } 331 if (!base::CreateTemporaryDirInDir(base_dir, sub_dir.value(), 332 &extension_dir)) { 333 ADD_FAILURE(); 334 return NULL; 335 } 336 std::string error; 337 scoped_refptr<Extension> extension = 338 Extension::Create(extension_dir, Manifest::INTERNAL, source, 339 Extension::NO_FLAGS, &error); 340 if (!error.empty()) { 341 ADD_FAILURE() << error; 342 return NULL; 343 } 344 if (!extension.get()) { 345 ADD_FAILURE(); 346 return NULL; 347 } 348 if (extension->name() != name) { 349 EXPECT_EQ(name, extension->name()); 350 return NULL; 351 } 352 if (extension->GetType() != type) { 353 EXPECT_EQ(type, extension->GetType()); 354 return NULL; 355 } 356 return extension; 357 } 358 359 } // namespace 360 361 scoped_refptr<Extension> SyncExtensionHelper::GetExtension( 362 Profile* profile, const std::string& name, Manifest::Type type) { 363 if (name.empty()) { 364 ADD_FAILURE(); 365 return NULL; 366 } 367 ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile); 368 if (it == profile_extensions_.end()) { 369 ADD_FAILURE(); 370 return NULL; 371 } 372 ExtensionNameMap::const_iterator it2 = it->second.find(name); 373 if (it2 != it->second.end()) { 374 return it2->second; 375 } 376 377 scoped_refptr<Extension> extension = 378 CreateExtension(extensions::ExtensionSystem::Get(profile) 379 ->extension_service() 380 ->install_directory(), 381 name, 382 type); 383 if (!extension.get()) { 384 ADD_FAILURE(); 385 return NULL; 386 } 387 const std::string& expected_id = crx_file::id_util::GenerateId(name); 388 if (extension->id() != expected_id) { 389 EXPECT_EQ(expected_id, extension->id()); 390 return NULL; 391 } 392 DVLOG(2) << "created extension with name = " 393 << name << ", id = " << expected_id; 394 (it->second)[name] = extension; 395 id_to_name_[expected_id] = name; 396 id_to_type_[expected_id] = type; 397 return extension; 398 } 399