• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ExtensionState()32 SyncExtensionHelper::ExtensionState::ExtensionState()
33     : enabled_state(ENABLED), incognito_enabled(false) {}
34 
~ExtensionState()35 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
36 
Equals(const SyncExtensionHelper::ExtensionState & other) const37 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
GetInstance()44 SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
45   SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
46   instance->SetupIfNecessary(sync_datatype_helper::test());
47   return instance;
48 }
49 
SyncExtensionHelper()50 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
51 
~SyncExtensionHelper()52 SyncExtensionHelper::~SyncExtensionHelper() {}
53 
SetupIfNecessary(SyncTest * test)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 
InstallExtension(Profile * profile,const std::string & name,Manifest::Type type)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 
UninstallExtension(Profile * profile,const std::string & name)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 
GetInstalledExtensionNames(Profile * profile) const88 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 
EnableExtension(Profile * profile,const std::string & name)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 
DisableExtension(Profile * profile,const std::string & name)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 
IsExtensionEnabled(Profile * profile,const std::string & name) const118 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 
IncognitoEnableExtension(Profile * profile,const std::string & name)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 
IncognitoDisableExtension(Profile * profile,const std::string & name)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 
IsIncognitoEnabled(Profile * profile,const std::string & name) const137 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 
IsExtensionPendingInstallForSync(Profile * profile,const std::string & id) const144 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 
InstallExtensionsPendingForSync(Profile * profile)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
GetExtensionStates(Profile * profile)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 
ExtensionStatesMatch(Profile * profile1,Profile * profile2)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 
SetupProfile(Profile * profile)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 
NameToPublicKey(const std::string & name)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.
CreateExtension(const base::FilePath & base_dir,const std::string & name,Manifest::Type type)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 
GetExtension(Profile * profile,const std::string & name,Manifest::Type type)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