Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / sync / test / integration / sync_extension_helper.cc
blob2b10e2d209a0a4cac6386b627e6e8e1321ee8e98
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.
5 #include "chrome/browser/sync/test/integration/sync_extension_helper.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/guid.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_util.h"
16 #include "chrome/browser/extensions/pending_extension_info.h"
17 #include "chrome/browser/extensions/pending_extension_manager.h"
18 #include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
21 #include "chrome/browser/sync/test/integration/sync_test.h"
22 #include "components/crx_file/id_util.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/install_flag.h"
27 #include "extensions/browser/uninstall_reason.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_set.h"
30 #include "extensions/common/manifest_constants.h"
31 #include "sync/api/string_ordinal.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 using extensions::Extension;
35 using extensions::ExtensionPrefs;
36 using extensions::ExtensionRegistry;
37 using extensions::Manifest;
39 const char kFakeExtensionPrefix[] = "fakeextension";
41 SyncExtensionHelper::ExtensionState::ExtensionState()
42 : enabled_state(ENABLED), disable_reasons(0), incognito_enabled(false) {}
44 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
46 bool SyncExtensionHelper::ExtensionState::Equals(
47 const SyncExtensionHelper::ExtensionState &other) const {
48 return ((enabled_state == other.enabled_state) &&
49 (disable_reasons == other.disable_reasons) &&
50 (incognito_enabled == other.incognito_enabled));
53 // static
54 SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
55 SyncExtensionHelper* instance = base::Singleton<SyncExtensionHelper>::get();
56 instance->SetupIfNecessary(sync_datatype_helper::test());
57 return instance;
60 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
62 SyncExtensionHelper::~SyncExtensionHelper() {}
64 void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) {
65 if (setup_completed_)
66 return;
68 extension_name_prefix_ = kFakeExtensionPrefix + base::GenerateGUID();
69 for (int i = 0; i < test->num_clients(); ++i) {
70 SetupProfile(test->GetProfile(i));
72 if (test->use_verifier()) {
73 SetupProfile(test->verifier());
76 setup_completed_ = true;
79 std::string SyncExtensionHelper::InstallExtension(
80 Profile* profile, const std::string& name, Manifest::Type type) {
81 scoped_refptr<Extension> extension = GetExtension(profile, name, type);
82 if (!extension.get()) {
83 NOTREACHED() << "Could not install extension " << name;
84 return std::string();
86 extensions::ExtensionSystem::Get(profile)
87 ->extension_service()
88 ->OnExtensionInstalled(extension.get(),
89 syncer::StringOrdinal(),
90 extensions::kInstallFlagInstallImmediately);
91 return extension->id();
94 void SyncExtensionHelper::UninstallExtension(
95 Profile* profile, const std::string& name) {
96 ExtensionService::UninstallExtensionHelper(
97 extensions::ExtensionSystem::Get(profile)->extension_service(),
98 crx_file::id_util::GenerateId(name),
99 extensions::UNINSTALL_REASON_SYNC);
102 std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
103 Profile* profile) const {
104 std::vector<std::string> names;
106 scoped_ptr<const extensions::ExtensionSet> extensions(
107 extensions::ExtensionRegistry::Get(profile)
108 ->GenerateInstalledExtensionsSet());
109 for (extensions::ExtensionSet::const_iterator it = extensions->begin();
110 it != extensions->end(); ++it) {
111 names.push_back((*it)->name());
114 return names;
117 void SyncExtensionHelper::EnableExtension(Profile* profile,
118 const std::string& name) {
119 extensions::ExtensionSystem::Get(profile)
120 ->extension_service()
121 ->EnableExtension(crx_file::id_util::GenerateId(name));
124 void SyncExtensionHelper::DisableExtension(Profile* profile,
125 const std::string& name) {
126 extensions::ExtensionSystem::Get(profile)
127 ->extension_service()
128 ->DisableExtension(crx_file::id_util::GenerateId(name),
129 Extension::DISABLE_USER_ACTION);
132 bool SyncExtensionHelper::IsExtensionEnabled(
133 Profile* profile, const std::string& name) const {
134 return extensions::ExtensionSystem::Get(profile)
135 ->extension_service()
136 ->IsExtensionEnabled(crx_file::id_util::GenerateId(name));
139 void SyncExtensionHelper::IncognitoEnableExtension(
140 Profile* profile, const std::string& name) {
141 extensions::util::SetIsIncognitoEnabled(
142 crx_file::id_util::GenerateId(name), profile, true);
145 void SyncExtensionHelper::IncognitoDisableExtension(
146 Profile* profile, const std::string& name) {
147 extensions::util::SetIsIncognitoEnabled(
148 crx_file::id_util::GenerateId(name), profile, false);
151 bool SyncExtensionHelper::IsIncognitoEnabled(
152 Profile* profile, const std::string& name) const {
153 return extensions::util::IsIncognitoEnabled(
154 crx_file::id_util::GenerateId(name), profile);
158 bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
159 Profile* profile, const std::string& id) const {
160 const extensions::PendingExtensionManager* pending_extension_manager =
161 extensions::ExtensionSystem::Get(profile)
162 ->extension_service()
163 ->pending_extension_manager();
164 const extensions::PendingExtensionInfo* info =
165 pending_extension_manager->GetById(id);
166 if (!info)
167 return false;
168 return info->is_from_sync();
171 void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile* profile) {
172 // TODO(akalin): Mock out the servers that the extensions auto-update
173 // mechanism talk to so as to more closely match what actually happens.
174 // Background networking will need to be re-enabled for extensions tests.
176 // We make a copy here since InstallExtension() removes the
177 // extension from the extensions service's copy.
178 const extensions::PendingExtensionManager* pending_extension_manager =
179 extensions::ExtensionSystem::Get(profile)
180 ->extension_service()
181 ->pending_extension_manager();
183 std::list<std::string> pending_crx_ids;
184 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
186 std::list<std::string>::const_iterator iter;
187 const extensions::PendingExtensionInfo* info = NULL;
188 for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) {
189 ASSERT_TRUE((info = pending_extension_manager->GetById(*iter)));
190 if (!info->is_from_sync())
191 continue;
193 StringMap::const_iterator iter2 = id_to_name_.find(*iter);
194 if (iter2 == id_to_name_.end()) {
195 ADD_FAILURE() << "Could not get name for id " << *iter
196 << " (profile = " << profile->GetDebugName() << ")";
197 continue;
199 TypeMap::const_iterator iter3 = id_to_type_.find(*iter);
200 if (iter3 == id_to_type_.end()) {
201 ADD_FAILURE() << "Could not get type for id " << *iter
202 << " (profile = " << profile->GetDebugName() << ")";
204 InstallExtension(profile, iter2->second, iter3->second);
208 SyncExtensionHelper::ExtensionStateMap
209 SyncExtensionHelper::GetExtensionStates(Profile* profile) {
210 const std::string& profile_debug_name = profile->GetDebugName();
212 ExtensionStateMap extension_state_map;
214 scoped_ptr<const extensions::ExtensionSet> extensions(
215 extensions::ExtensionRegistry::Get(profile)
216 ->GenerateInstalledExtensionsSet());
218 ExtensionService* extension_service =
219 extensions::ExtensionSystem::Get(profile)->extension_service();
220 for (const scoped_refptr<const Extension>& extension : *extensions) {
221 const std::string& id = extension->id();
222 // When doing Chrome account sign in though the Gaia extension, the Gaia
223 // extensions gets installed once and used by multiple profiles. This will
224 // cause extension list of profiles to not match.
225 if (id == extensions::kGaiaAuthExtensionId)
226 continue;
227 ExtensionState& extension_state = extension_state_map[id];
228 extension_state.enabled_state =
229 extension_service->IsExtensionEnabled(id) ?
230 ExtensionState::ENABLED :
231 ExtensionState::DISABLED;
232 extension_state.disable_reasons =
233 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
234 extension_state.incognito_enabled =
235 extensions::util::IsIncognitoEnabled(id, profile);
237 DVLOG(2) << "Extension " << id << " in profile " << profile_debug_name
238 << " is " << (extension_service->IsExtensionEnabled(id) ?
239 "enabled" : "disabled");
242 const extensions::PendingExtensionManager* pending_extension_manager =
243 extension_service->pending_extension_manager();
245 std::list<std::string> pending_crx_ids;
246 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
248 for (const std::string& id : pending_crx_ids) {
249 ExtensionState& extension_state = extension_state_map[id];
250 extension_state.enabled_state = ExtensionState::PENDING;
251 extension_state.disable_reasons =
252 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
253 extension_state.incognito_enabled =
254 extensions::util::IsIncognitoEnabled(id, profile);
255 DVLOG(2) << "Extension " << id << " in profile "
256 << profile_debug_name << " is pending";
259 return extension_state_map;
262 bool SyncExtensionHelper::ExtensionStatesMatch(
263 Profile* profile1, Profile* profile2) {
264 const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
265 const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
266 if (state_map1.size() != state_map2.size()) {
267 DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
268 << " does not match profile " << profile2->GetDebugName();
269 return false;
272 ExtensionStateMap::const_iterator it1 = state_map1.begin();
273 ExtensionStateMap::const_iterator it2 = state_map2.begin();
274 while (it1 != state_map1.end()) {
275 if (it1->first != it2->first) {
276 DVLOG(1) << "Extensions for profile " << profile1->GetDebugName()
277 << " do not match profile " << profile2->GetDebugName();
278 return false;
279 } else if (!sync_datatype_helper::test()->UsingExternalServers() &&
280 !it1->second.Equals(it2->second)) {
281 // If this test is run against real backend servers then we do not expect
282 // to install pending extensions. So, we don't check equality of
283 // ExtensionState of each extension per profile.
284 DVLOG(1) << "Extension states for profile " << profile1->GetDebugName()
285 << " do not match profile " << profile2->GetDebugName();
286 return false;
288 ++it1;
289 ++it2;
291 return true;
294 std::string SyncExtensionHelper::CreateFakeExtensionName(int index) {
295 return extension_name_prefix_ + base::IntToString(index);
298 bool SyncExtensionHelper::ExtensionNameToIndex(const std::string& name,
299 int* index) {
300 if (!(base::StartsWith(name, extension_name_prefix_,
301 base::CompareCase::SENSITIVE) &&
302 base::StringToInt(name.substr(extension_name_prefix_.size()), index))) {
303 LOG(WARNING) << "Unable to convert extension name \"" << name
304 << "\" to index";
305 return false;
307 return true;
310 void SyncExtensionHelper::SetupProfile(Profile* profile) {
311 extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
312 profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
315 namespace {
317 std::string NameToPublicKey(const std::string& name) {
318 std::string public_key;
319 std::string pem;
320 EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
321 Extension::FormatPEMForFileOutput(pem, &public_key,
322 true /* is_public */));
323 return public_key;
326 // TODO(akalin): Somehow unify this with MakeExtension() in
327 // extension_util_unittest.cc.
328 scoped_refptr<Extension> CreateExtension(const base::FilePath& base_dir,
329 const std::string& name,
330 Manifest::Type type) {
331 base::DictionaryValue source;
332 source.SetString(extensions::manifest_keys::kName, name);
333 const std::string& public_key = NameToPublicKey(name);
334 source.SetString(extensions::manifest_keys::kPublicKey, public_key);
335 source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
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 new base::DictionaryValue());
343 break;
344 case Manifest::TYPE_HOSTED_APP:
345 case Manifest::TYPE_LEGACY_PACKAGED_APP:
346 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
347 source.SetString(extensions::manifest_keys::kLaunchWebURL,
348 "http://www.example.com");
349 break;
350 case Manifest::TYPE_PLATFORM_APP: {
351 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
352 source.Set(extensions::manifest_keys::kPlatformAppBackground,
353 new base::DictionaryValue());
354 base::ListValue* scripts = new base::ListValue();
355 scripts->AppendString("main.js");
356 source.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts,
357 scripts);
358 break;
360 default:
361 ADD_FAILURE();
362 return NULL;
364 const base::FilePath sub_dir = base::FilePath().AppendASCII(name);
365 base::FilePath extension_dir;
366 if (!base::PathExists(base_dir) &&
367 !base::CreateDirectory(base_dir)) {
368 ADD_FAILURE();
369 return NULL;
371 if (!base::CreateTemporaryDirInDir(base_dir, sub_dir.value(),
372 &extension_dir)) {
373 ADD_FAILURE();
374 return NULL;
376 std::string error;
377 scoped_refptr<Extension> extension =
378 Extension::Create(extension_dir, Manifest::INTERNAL, source,
379 Extension::NO_FLAGS, &error);
380 if (!error.empty()) {
381 ADD_FAILURE() << error;
382 return NULL;
384 if (!extension.get()) {
385 ADD_FAILURE();
386 return NULL;
388 if (extension->name() != name) {
389 EXPECT_EQ(name, extension->name());
390 return NULL;
392 if (extension->GetType() != type) {
393 EXPECT_EQ(type, extension->GetType());
394 return NULL;
396 return extension;
399 } // namespace
401 scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
402 Profile* profile, const std::string& name, Manifest::Type type) {
403 if (name.empty()) {
404 ADD_FAILURE();
405 return NULL;
407 ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
408 if (it == profile_extensions_.end()) {
409 ADD_FAILURE();
410 return NULL;
412 ExtensionNameMap::const_iterator it2 = it->second.find(name);
413 if (it2 != it->second.end()) {
414 return it2->second;
417 scoped_refptr<Extension> extension =
418 CreateExtension(extensions::ExtensionSystem::Get(profile)
419 ->extension_service()
420 ->install_directory(),
421 name,
422 type);
423 if (!extension.get()) {
424 ADD_FAILURE();
425 return NULL;
427 const std::string& expected_id = crx_file::id_util::GenerateId(name);
428 if (extension->id() != expected_id) {
429 EXPECT_EQ(expected_id, extension->id());
430 return NULL;
432 DVLOG(2) << "created extension with name = "
433 << name << ", id = " << expected_id;
434 (it->second)[name] = extension;
435 id_to_name_[expected_id] = name;
436 id_to_type_[expected_id] = type;
437 return extension;