Extensions: Store disable reasons in Sync
[chromium-blink-merge.git] / chrome / browser / sync / test / integration / sync_extension_helper.cc
blobd51c5a940162f86326eb86a7d8bcd2864732b175
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/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 "components/crx_file/id_util.h"
19 #include "extensions/browser/extension_prefs.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/browser/install_flag.h"
23 #include "extensions/browser/uninstall_reason.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_set.h"
26 #include "extensions/common/manifest_constants.h"
27 #include "sync/api/string_ordinal.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 using extensions::Extension;
31 using extensions::ExtensionPrefs;
32 using extensions::ExtensionRegistry;
33 using extensions::Manifest;
35 SyncExtensionHelper::ExtensionState::ExtensionState()
36 : enabled_state(ENABLED), disable_reasons(0), incognito_enabled(false) {}
38 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
40 bool SyncExtensionHelper::ExtensionState::Equals(
41 const SyncExtensionHelper::ExtensionState &other) const {
42 return ((enabled_state == other.enabled_state) &&
43 (disable_reasons == other.disable_reasons) &&
44 (incognito_enabled == other.incognito_enabled));
47 // static
48 SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
49 SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
50 instance->SetupIfNecessary(sync_datatype_helper::test());
51 return instance;
54 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
56 SyncExtensionHelper::~SyncExtensionHelper() {}
58 void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) {
59 if (setup_completed_)
60 return;
62 for (int i = 0; i < test->num_clients(); ++i) {
63 SetupProfile(test->GetProfile(i));
65 SetupProfile(test->verifier());
67 setup_completed_ = true;
70 std::string SyncExtensionHelper::InstallExtension(
71 Profile* profile, const std::string& name, Manifest::Type type) {
72 scoped_refptr<Extension> extension = GetExtension(profile, name, type);
73 if (!extension.get()) {
74 NOTREACHED() << "Could not install extension " << name;
75 return std::string();
77 extensions::ExtensionSystem::Get(profile)
78 ->extension_service()
79 ->OnExtensionInstalled(extension.get(),
80 syncer::StringOrdinal(),
81 extensions::kInstallFlagInstallImmediately);
82 return extension->id();
85 void SyncExtensionHelper::UninstallExtension(
86 Profile* profile, const std::string& name) {
87 ExtensionService::UninstallExtensionHelper(
88 extensions::ExtensionSystem::Get(profile)->extension_service(),
89 crx_file::id_util::GenerateId(name),
90 extensions::UNINSTALL_REASON_SYNC);
93 std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
94 Profile* profile) const {
95 std::vector<std::string> names;
97 scoped_ptr<const extensions::ExtensionSet> extensions(
98 extensions::ExtensionRegistry::Get(profile)
99 ->GenerateInstalledExtensionsSet());
100 for (extensions::ExtensionSet::const_iterator it = extensions->begin();
101 it != extensions->end(); ++it) {
102 names.push_back((*it)->name());
105 return names;
108 void SyncExtensionHelper::EnableExtension(Profile* profile,
109 const std::string& name) {
110 extensions::ExtensionSystem::Get(profile)
111 ->extension_service()
112 ->EnableExtension(crx_file::id_util::GenerateId(name));
115 void SyncExtensionHelper::DisableExtension(Profile* profile,
116 const std::string& name) {
117 extensions::ExtensionSystem::Get(profile)
118 ->extension_service()
119 ->DisableExtension(crx_file::id_util::GenerateId(name),
120 Extension::DISABLE_USER_ACTION);
123 bool SyncExtensionHelper::IsExtensionEnabled(
124 Profile* profile, const std::string& name) const {
125 return extensions::ExtensionSystem::Get(profile)
126 ->extension_service()
127 ->IsExtensionEnabled(crx_file::id_util::GenerateId(name));
130 void SyncExtensionHelper::IncognitoEnableExtension(
131 Profile* profile, const std::string& name) {
132 extensions::util::SetIsIncognitoEnabled(
133 crx_file::id_util::GenerateId(name), profile, true);
136 void SyncExtensionHelper::IncognitoDisableExtension(
137 Profile* profile, const std::string& name) {
138 extensions::util::SetIsIncognitoEnabled(
139 crx_file::id_util::GenerateId(name), profile, false);
142 bool SyncExtensionHelper::IsIncognitoEnabled(
143 Profile* profile, const std::string& name) const {
144 return extensions::util::IsIncognitoEnabled(
145 crx_file::id_util::GenerateId(name), profile);
149 bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
150 Profile* profile, const std::string& id) const {
151 const extensions::PendingExtensionManager* pending_extension_manager =
152 extensions::ExtensionSystem::Get(profile)
153 ->extension_service()
154 ->pending_extension_manager();
155 const extensions::PendingExtensionInfo* info =
156 pending_extension_manager->GetById(id);
157 if (!info)
158 return false;
159 return info->is_from_sync();
162 void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile* profile) {
163 // TODO(akalin): Mock out the servers that the extensions auto-update
164 // mechanism talk to so as to more closely match what actually happens.
165 // Background networking will need to be re-enabled for extensions tests.
167 // We make a copy here since InstallExtension() removes the
168 // extension from the extensions service's copy.
169 const extensions::PendingExtensionManager* pending_extension_manager =
170 extensions::ExtensionSystem::Get(profile)
171 ->extension_service()
172 ->pending_extension_manager();
174 std::list<std::string> pending_crx_ids;
175 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
177 std::list<std::string>::const_iterator iter;
178 const extensions::PendingExtensionInfo* info = NULL;
179 for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) {
180 ASSERT_TRUE((info = pending_extension_manager->GetById(*iter)));
181 if (!info->is_from_sync())
182 continue;
184 StringMap::const_iterator iter2 = id_to_name_.find(*iter);
185 if (iter2 == id_to_name_.end()) {
186 ADD_FAILURE() << "Could not get name for id " << *iter
187 << " (profile = " << profile->GetDebugName() << ")";
188 continue;
190 TypeMap::const_iterator iter3 = id_to_type_.find(*iter);
191 if (iter3 == id_to_type_.end()) {
192 ADD_FAILURE() << "Could not get type for id " << *iter
193 << " (profile = " << profile->GetDebugName() << ")";
195 InstallExtension(profile, iter2->second, iter3->second);
199 SyncExtensionHelper::ExtensionStateMap
200 SyncExtensionHelper::GetExtensionStates(Profile* profile) {
201 const std::string& profile_debug_name = profile->GetDebugName();
203 ExtensionStateMap extension_state_map;
205 scoped_ptr<const extensions::ExtensionSet> extensions(
206 extensions::ExtensionRegistry::Get(profile)
207 ->GenerateInstalledExtensionsSet());
209 ExtensionService* extension_service =
210 extensions::ExtensionSystem::Get(profile)->extension_service();
211 for (const scoped_refptr<const Extension>& extension : *extensions) {
212 const std::string& id = extension->id();
213 ExtensionState& extension_state = extension_state_map[id];
214 extension_state.enabled_state =
215 extension_service->IsExtensionEnabled(id) ?
216 ExtensionState::ENABLED :
217 ExtensionState::DISABLED;
218 extension_state.disable_reasons =
219 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
220 extension_state.incognito_enabled =
221 extensions::util::IsIncognitoEnabled(id, profile);
223 DVLOG(2) << "Extension " << id << " in profile " << profile_debug_name
224 << " is " << (extension_service->IsExtensionEnabled(id) ?
225 "enabled" : "disabled");
228 const extensions::PendingExtensionManager* pending_extension_manager =
229 extension_service->pending_extension_manager();
231 std::list<std::string> pending_crx_ids;
232 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
234 for (const std::string& id : pending_crx_ids) {
235 ExtensionState& extension_state = extension_state_map[id];
236 extension_state.enabled_state = ExtensionState::PENDING;
237 extension_state.disable_reasons =
238 ExtensionPrefs::Get(profile)->GetDisableReasons(id);
239 extension_state.incognito_enabled =
240 extensions::util::IsIncognitoEnabled(id, profile);
241 DVLOG(2) << "Extension " << id << " in profile "
242 << profile_debug_name << " is pending";
245 return extension_state_map;
248 bool SyncExtensionHelper::ExtensionStatesMatch(
249 Profile* profile1, Profile* profile2) {
250 const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
251 const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
252 if (state_map1.size() != state_map2.size()) {
253 DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
254 << " does not match profile " << profile2->GetDebugName();
255 return false;
258 ExtensionStateMap::const_iterator it1 = state_map1.begin();
259 ExtensionStateMap::const_iterator it2 = state_map2.begin();
260 while (it1 != state_map1.end()) {
261 if (it1->first != it2->first) {
262 DVLOG(1) << "Extensions for profile " << profile1->GetDebugName()
263 << " do not match profile " << profile2->GetDebugName();
264 return false;
265 } else if (!it1->second.Equals(it2->second)) {
266 DVLOG(1) << "Extension states for profile " << profile1->GetDebugName()
267 << " do not match profile " << profile2->GetDebugName();
268 return false;
270 ++it1;
271 ++it2;
273 return true;
276 void SyncExtensionHelper::SetupProfile(Profile* profile) {
277 extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
278 profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
281 namespace {
283 std::string NameToPublicKey(const std::string& name) {
284 std::string public_key;
285 std::string pem;
286 EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
287 Extension::FormatPEMForFileOutput(pem, &public_key,
288 true /* is_public */));
289 return public_key;
292 // TODO(akalin): Somehow unify this with MakeExtension() in
293 // extension_util_unittest.cc.
294 scoped_refptr<Extension> CreateExtension(const base::FilePath& base_dir,
295 const std::string& name,
296 Manifest::Type type) {
297 base::DictionaryValue source;
298 source.SetString(extensions::manifest_keys::kName, name);
299 const std::string& public_key = NameToPublicKey(name);
300 source.SetString(extensions::manifest_keys::kPublicKey, public_key);
301 source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
302 switch (type) {
303 case Manifest::TYPE_EXTENSION:
304 // Do nothing.
305 break;
306 case Manifest::TYPE_THEME:
307 source.Set(extensions::manifest_keys::kTheme,
308 new base::DictionaryValue());
309 break;
310 case Manifest::TYPE_HOSTED_APP:
311 case Manifest::TYPE_LEGACY_PACKAGED_APP:
312 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
313 source.SetString(extensions::manifest_keys::kLaunchWebURL,
314 "http://www.example.com");
315 break;
316 case Manifest::TYPE_PLATFORM_APP: {
317 source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
318 source.Set(extensions::manifest_keys::kPlatformAppBackground,
319 new base::DictionaryValue());
320 base::ListValue* scripts = new base::ListValue();
321 scripts->AppendString("main.js");
322 source.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts,
323 scripts);
324 break;
326 default:
327 ADD_FAILURE();
328 return NULL;
330 const base::FilePath sub_dir = base::FilePath().AppendASCII(name);
331 base::FilePath extension_dir;
332 if (!base::PathExists(base_dir) &&
333 !base::CreateDirectory(base_dir)) {
334 ADD_FAILURE();
335 return NULL;
337 if (!base::CreateTemporaryDirInDir(base_dir, sub_dir.value(),
338 &extension_dir)) {
339 ADD_FAILURE();
340 return NULL;
342 std::string error;
343 scoped_refptr<Extension> extension =
344 Extension::Create(extension_dir, Manifest::INTERNAL, source,
345 Extension::NO_FLAGS, &error);
346 if (!error.empty()) {
347 ADD_FAILURE() << error;
348 return NULL;
350 if (!extension.get()) {
351 ADD_FAILURE();
352 return NULL;
354 if (extension->name() != name) {
355 EXPECT_EQ(name, extension->name());
356 return NULL;
358 if (extension->GetType() != type) {
359 EXPECT_EQ(type, extension->GetType());
360 return NULL;
362 return extension;
365 } // namespace
367 scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
368 Profile* profile, const std::string& name, Manifest::Type type) {
369 if (name.empty()) {
370 ADD_FAILURE();
371 return NULL;
373 ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
374 if (it == profile_extensions_.end()) {
375 ADD_FAILURE();
376 return NULL;
378 ExtensionNameMap::const_iterator it2 = it->second.find(name);
379 if (it2 != it->second.end()) {
380 return it2->second;
383 scoped_refptr<Extension> extension =
384 CreateExtension(extensions::ExtensionSystem::Get(profile)
385 ->extension_service()
386 ->install_directory(),
387 name,
388 type);
389 if (!extension.get()) {
390 ADD_FAILURE();
391 return NULL;
393 const std::string& expected_id = crx_file::id_util::GenerateId(name);
394 if (extension->id() != expected_id) {
395 EXPECT_EQ(expected_id, extension->id());
396 return NULL;
398 DVLOG(2) << "created extension with name = "
399 << name << ", id = " << expected_id;
400 (it->second)[name] = extension;
401 id_to_name_[expected_id] = name;
402 id_to_type_[expected_id] = type;
403 return extension;