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
));
49 SyncExtensionHelper
* SyncExtensionHelper::GetInstance() {
50 SyncExtensionHelper
* instance
= Singleton
<SyncExtensionHelper
>::get();
51 instance
->SetupIfNecessary(sync_datatype_helper::test());
55 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
57 SyncExtensionHelper::~SyncExtensionHelper() {}
59 void SyncExtensionHelper::SetupIfNecessary(SyncTest
* test
) {
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
;
80 extensions::ExtensionSystem::Get(profile
)
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());
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
);
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())
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() << ")";
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
)
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();
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();
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();
288 void SyncExtensionHelper::SetupProfile(Profile
* profile
) {
289 extensions::ExtensionSystem::Get(profile
)->InitForRegularProfile(true);
290 profile_extensions_
.insert(make_pair(profile
, ExtensionNameMap()));
295 std::string
NameToPublicKey(const std::string
& name
) {
296 std::string public_key
;
298 EXPECT_TRUE(Extension::ProducePEM(name
, &pem
) &&
299 Extension::FormatPEMForFileOutput(pem
, &public_key
,
300 true /* is_public */));
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");
315 case Manifest::TYPE_EXTENSION
:
318 case Manifest::TYPE_THEME
:
319 source
.Set(extensions::manifest_keys::kTheme
,
320 new base::DictionaryValue());
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");
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
,
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
)) {
349 if (!base::CreateTemporaryDirInDir(base_dir
, sub_dir
.value(),
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
;
362 if (!extension
.get()) {
366 if (extension
->name() != name
) {
367 EXPECT_EQ(name
, extension
->name());
370 if (extension
->GetType() != type
) {
371 EXPECT_EQ(type
, extension
->GetType());
379 scoped_refptr
<Extension
> SyncExtensionHelper::GetExtension(
380 Profile
* profile
, const std::string
& name
, Manifest::Type type
) {
385 ProfileExtensionNameMap::iterator it
= profile_extensions_
.find(profile
);
386 if (it
== profile_extensions_
.end()) {
390 ExtensionNameMap::const_iterator it2
= it
->second
.find(name
);
391 if (it2
!= it
->second
.end()) {
395 scoped_refptr
<Extension
> extension
=
396 CreateExtension(extensions::ExtensionSystem::Get(profile
)
397 ->extension_service()
398 ->install_directory(),
401 if (!extension
.get()) {
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());
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
;