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_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 
     28 using extensions::Extension;
     29 using extensions::ExtensionRegistry;
     30 using extensions::Manifest;
     31 
     32 SyncExtensionHelper::ExtensionState::ExtensionState()
     33     : enabled_state(ENABLED), incognito_enabled(false) {}
     34 
     35 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
     36 
     37 bool 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
     44 SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
     45   SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
     46   instance->SetupIfNecessary(sync_datatype_helper::test());
     47   return instance;
     48 }
     49 
     50 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
     51 
     52 SyncExtensionHelper::~SyncExtensionHelper() {}
     53 
     54 void 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 
     66 std::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 
     81 void 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 
     88 std::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 
    103 void 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 
    110 void 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 
    118 bool 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 
    125 void SyncExtensionHelper::IncognitoEnableExtension(
    126     Profile* profile, const std::string& name) {
    127   extensions::util::SetIsIncognitoEnabled(
    128       extensions::id_util::GenerateId(name), profile, true);
    129 }
    130 
    131 void SyncExtensionHelper::IncognitoDisableExtension(
    132     Profile* profile, const std::string& name) {
    133   extensions::util::SetIsIncognitoEnabled(
    134       extensions::id_util::GenerateId(name), profile, false);
    135 }
    136 
    137 bool 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 
    144 bool 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 
    157 void 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 
    194 SyncExtensionHelper::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 
    240 bool 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 
    268 void SyncExtensionHelper::SetupProfile(Profile* profile) {
    269   extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
    270   profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
    271 }
    272 
    273 namespace {
    274 
    275 std::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.
    286 scoped_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 
    359 scoped_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