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