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