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