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