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