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