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