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