Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / sync / test / integration / sync_extension_helper.cc
blobbeec36f0242bb807a18946a665cf256158f6c77a
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/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/extensions/signin/gaia_auth_extension_loader.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
18 #include "chrome/browser/sync/test/integration/sync_test.h"
19 #include "components/crx_file/id_util.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/install_flag.h"
24 #include "extensions/browser/uninstall_reason.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "sync/api/string_ordinal.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using extensions::Extension;
32 using extensions::ExtensionPrefs;
33 using extensions::ExtensionRegistry;
34 using extensions::Manifest;
36 SyncExtensionHelper::ExtensionState::ExtensionState()
37 : enabled_state(ENABLED), disable_reasons(0), incognito_enabled(false) {}
39 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
41 bool SyncExtensionHelper::ExtensionState::Equals(
42 const SyncExtensionHelper::ExtensionState &other) const {
43 return ((enabled_state == other.enabled_state) &&
44 (disable_reasons == other.disable_reasons) &&
45 (incognito_enabled == other.incognito_enabled));
48 // static
49 SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
50 SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
51 instance->SetupIfNecessary(sync_datatype_helper::test());
52 return instance;
55 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
57 SyncExtensionHelper::~SyncExtensionHelper() {}
59 void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) {
60 if (setup_completed_)
61 return;
63 for (int i = 0; i < test->num_clients(); ++i) {
64 SetupProfile(test->GetProfile(i));
66 if (test->use_verifier()) {
67 SetupProfile(test->verifier());
70 setup_completed_ = true;
73 std::string SyncExtensionHelper::InstallExtension(
74 Profile* profile, const std::string& name, Manifest::Type type) {
75 scoped_refptr<Extension> extension = GetExtension(profile, name, type);
76 if (!extension.get()) {
77 NOTREACHED() << "Could not install extension " << name;
78 return std::string();
80 extensions::ExtensionSystem::Get(profile)
81 ->extension_service()
82 ->OnExtensionInstalled(extension.get(),
83 syncer::StringOrdinal(),
84 extensions::kInstallFlagInstallImmediately);
85 return extension->id();
88 void SyncExtensionHelper::UninstallExtension(
89 Profile* profile, const std::string& name) {
90 ExtensionService::UninstallExtensionHelper(
91 extensions::ExtensionSystem::Get(profile)->extension_service(),
92 crx_file::id_util::GenerateId(name),
93 extensions::UNINSTALL_REASON_SYNC);
96 std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
97 Profile* profile) const {
98 std::vector<std::string> names;
100 scoped_ptr<const extensions::ExtensionSet> extensions(
101 extensions::ExtensionRegistry::Get(profile)
102 ->GenerateInstalledExtensionsSet());
103 for (extensions::ExtensionSet::const_iterator it = extensions->begin();
104 it != extensions->end(); ++it) {
105 names.push_back((*it)->name());
108 return names;
111 void SyncExtensionHelper::EnableExtension(Profile* profile,
112 const std::string& name) {
113 extensions::ExtensionSystem::Get(profile)
114 ->extension_service()
115 ->EnableExtension(crx_file::id_util::GenerateId(name));
118 void SyncExtensionHelper::DisableExtension(Profile* profile,
119 const std::string& name) {
120 extensions::ExtensionSystem::Get(profile)
121 ->extension_service()
122 ->DisableExtension(crx_file::id_util::GenerateId(name),
123 Extension::DISABLE_USER_ACTION);
126 bool SyncExtensionHelper::IsExtensionEnabled(
127 Profile* profile, const std::string& name) const {
128 return extensions::ExtensionSystem::Get(profile)
129 ->extension_service()
130 ->IsExtensionEnabled(crx_file::id_util::GenerateId(name));
133 void SyncExtensionHelper::IncognitoEnableExtension(
134 Profile* profile, const std::string& name) {
135 extensions::util::SetIsIncognitoEnabled(
136 crx_file::id_util::GenerateId(name), profile, true);
139 void SyncExtensionHelper::IncognitoDisableExtension(
140 Profile* profile, const std::string& name) {
141 extensions::util::SetIsIncognitoEnabled(
142 crx_file::id_util::GenerateId(name), profile, false);
145 bool SyncExtensionHelper::IsIncognitoEnabled(
146 Profile* profile, const std::string& name) const {
147 return extensions::util::IsIncognitoEnabled(
148 crx_file::id_util::GenerateId(name), profile);
152 bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
153 Profile* profile, const std::string& id) const {
154 const extensions::PendingExtensionManager* pending_extension_manager =
155 extensions::ExtensionSystem::Get(profile)
156 ->extension_service()
157 ->pending_extension_manager();
158 const extensions::PendingExtensionInfo* info =
159 pending_extension_manager->GetById(id);
160 if (!info)
161 return false;
162 return info->is_from_sync();
165 void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile* profile) {
166 // TODO(akalin): Mock out the servers that the extensions auto-update
167 // mechanism talk to so as to more closely match what actually happens.
168 // Background networking will need to be re-enabled for extensions tests.
170 // We make a copy here since InstallExtension() removes the
171 // extension from the extensions service's copy.
172 const extensions::PendingExtensionManager* pending_extension_manager =
173 extensions::ExtensionSystem::Get(profile)
174 ->extension_service()
175 ->pending_extension_manager();
177 std::list<std::string> pending_crx_ids;
178 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
180 std::list<std::string>::const_iterator iter;
181 const extensions::PendingExtensionInfo* info = NULL;
182 for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) {
183 ASSERT_TRUE((info = pending_extension_manager->GetById(*iter)));
184 if (!info->is_from_sync())
185 continue;
187 StringMap::const_iterator iter2 = id_to_name_.find(*iter);
188 if (iter2 == id_to_name_.end()) {
189 ADD_FAILURE() << "Could not get name for id " << *iter
190 << " (profile = " << profile->GetDebugName() << ")";
191 continue;
193 TypeMap::const_iterator iter3 = id_to_type_.find(*iter);
194 if (iter3 == id_to_type_.end()) {
195 ADD_FAILURE() << "Could not get type for id " << *iter
196 << " (profile = " << profile->GetDebugName() << ")";
198 InstallExtension(profile, iter2->second, iter3->second);
202 SyncExtensionHelper::ExtensionStateMap
203 SyncExtensionHelper::GetExtensionStates(Profile* profile) {
204 const std::string& profile_debug_name = profile->GetDebugName();
206 ExtensionStateMap extension_state_map;
208 scoped_ptr<const extensions::ExtensionSet> extensions(
209 extensions::ExtensionRegistry::Get(profile)
210 ->GenerateInstalledExtensionsSet());
212 ExtensionService* extension_service =
213 extensions::ExtensionSystem::Get(profile)->extension_service();
214 for (const scoped_refptr<const Extension>& extension : *extensions) {
215 const std::string& id = extension->id();
216 // When doing Chrome account sign in though the Gaia extension, the Gaia
217 // extensions gets installed once and used by multiple profiles. This will
218 // cause extension list of profiles to not match.
219 if (id == extensions::kGaiaAuthExtensionId)
220 continue;
221 ExtensionState& extension_state = extension_state_map[id];
222 extension_state.enabled_state =
223 extension_service->IsExtensionEnabled(id) ?
224 ExtensionState::ENABLED :
225 ExtensionState::DISABLED;
226 extension_state.disable_reasons =
227 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
228 extension_state.incognito_enabled =
229 extensions::util::IsIncognitoEnabled(id, profile);
231 DVLOG(2) << "Extension " << id << " in profile " << profile_debug_name
232 << " is " << (extension_service->IsExtensionEnabled(id) ?
233 "enabled" : "disabled");
236 const extensions::PendingExtensionManager* pending_extension_manager =
237 extension_service->pending_extension_manager();
239 std::list<std::string> pending_crx_ids;
240 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
242 for (const std::string& id : pending_crx_ids) {
243 ExtensionState& extension_state = extension_state_map[id];
244 extension_state.enabled_state = ExtensionState::PENDING;
245 extension_state.disable_reasons =
246 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
247 extension_state.incognito_enabled =
248 extensions::util::IsIncognitoEnabled(id, profile);
249 DVLOG(2) << "Extension " << id << " in profile "
250 << profile_debug_name << " is pending";
253 return extension_state_map;
256 bool SyncExtensionHelper::ExtensionStatesMatch(
257 Profile* profile1, Profile* profile2) {
258 const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
259 const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
260 if (state_map1.size() != state_map2.size()) {
261 DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
262 << " does not match profile " << profile2->GetDebugName();
263 return false;
266 ExtensionStateMap::const_iterator it1 = state_map1.begin();
267 ExtensionStateMap::const_iterator it2 = state_map2.begin();
268 while (it1 != state_map1.end()) {
269 if (it1->first != it2->first) {
270 DVLOG(1) << "Extensions for profile " << profile1->GetDebugName()
271 << " do not match profile " << profile2->GetDebugName();
272 return false;
273 } else if (!sync_datatype_helper::test()->UsingExternalServers() &&
274 !it1->second.Equals(it2->second)) {
275 // If this test is run against real backend servers then we do not expect
276 // to install pending extensions. So, we don't check equality of
277 // ExtensionState of each extension per profile.
278 DVLOG(1) << "Extension states for profile " << profile1->GetDebugName()
279 << " do not match profile " << profile2->GetDebugName();
280 return false;
282 ++it1;
283 ++it2;
285 return true;
288 void SyncExtensionHelper::SetupProfile(Profile* profile) {
289 extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
290 profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
293 namespace {
295 std::string NameToPublicKey(const std::string& name) {
296 std::string public_key;
297 std::string pem;
298 EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
299 Extension::FormatPEMForFileOutput(pem, &public_key,
300 true /* is_public */));
301 return public_key;
304 // TODO(akalin): Somehow unify this with MakeExtension() in
305 // extension_util_unittest.cc.
306 scoped_refptr<Extension> CreateExtension(const base::FilePath& base_dir,
307 const std::string& name,
308 Manifest::Type type) {
309 base::DictionaryValue source;
310 source.SetString(extensions::manifest_keys::kName, name);
311 const std::string& public_key = NameToPublicKey(name);
312 source.SetString(extensions::manifest_keys::kPublicKey, public_key);
313 source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
314 switch (type) {
315 case Manifest::TYPE_EXTENSION:
316 // Do nothing.
317 break;
318 case Manifest::TYPE_THEME:
319 source.Set(extensions::manifest_keys::kTheme,
320 new base::DictionaryValue());
321 break;
322 case Manifest::TYPE_HOSTED_APP:
323 case Manifest::TYPE_LEGACY_PACKAGED_APP:
324 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
325 source.SetString(extensions::manifest_keys::kLaunchWebURL,
326 "http://www.example.com");
327 break;
328 case Manifest::TYPE_PLATFORM_APP: {
329 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
330 source.Set(extensions::manifest_keys::kPlatformAppBackground,
331 new base::DictionaryValue());
332 base::ListValue* scripts = new base::ListValue();
333 scripts->AppendString("main.js");
334 source.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts,
335 scripts);
336 break;
338 default:
339 ADD_FAILURE();
340 return NULL;
342 const base::FilePath sub_dir = base::FilePath().AppendASCII(name);
343 base::FilePath extension_dir;
344 if (!base::PathExists(base_dir) &&
345 !base::CreateDirectory(base_dir)) {
346 ADD_FAILURE();
347 return NULL;
349 if (!base::CreateTemporaryDirInDir(base_dir, sub_dir.value(),
350 &extension_dir)) {
351 ADD_FAILURE();
352 return NULL;
354 std::string error;
355 scoped_refptr<Extension> extension =
356 Extension::Create(extension_dir, Manifest::INTERNAL, source,
357 Extension::NO_FLAGS, &error);
358 if (!error.empty()) {
359 ADD_FAILURE() << error;
360 return NULL;
362 if (!extension.get()) {
363 ADD_FAILURE();
364 return NULL;
366 if (extension->name() != name) {
367 EXPECT_EQ(name, extension->name());
368 return NULL;
370 if (extension->GetType() != type) {
371 EXPECT_EQ(type, extension->GetType());
372 return NULL;
374 return extension;
377 } // namespace
379 scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
380 Profile* profile, const std::string& name, Manifest::Type type) {
381 if (name.empty()) {
382 ADD_FAILURE();
383 return NULL;
385 ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
386 if (it == profile_extensions_.end()) {
387 ADD_FAILURE();
388 return NULL;
390 ExtensionNameMap::const_iterator it2 = it->second.find(name);
391 if (it2 != it->second.end()) {
392 return it2->second;
395 scoped_refptr<Extension> extension =
396 CreateExtension(extensions::ExtensionSystem::Get(profile)
397 ->extension_service()
398 ->install_directory(),
399 name,
400 type);
401 if (!extension.get()) {
402 ADD_FAILURE();
403 return NULL;
405 const std::string& expected_id = crx_file::id_util::GenerateId(name);
406 if (extension->id() != expected_id) {
407 EXPECT_EQ(expected_id, extension->id());
408 return NULL;
410 DVLOG(2) << "created extension with name = "
411 << name << ", id = " << expected_id;
412 (it->second)[name] = extension;
413 id_to_name_[expected_id] = name;
414 id_to_type_[expected_id] = type;
415 return extension;