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