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