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