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