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/file_util.h"
8 #include "base/files/file_path.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_system.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
16 #include "chrome/browser/sync/test/integration/sync_test.h"
17 #include "extensions/browser/pending_extension_info.h"
18 #include "extensions/browser/pending_extension_manager.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_set.h"
21 #include "extensions/common/id_util.h"
22 #include "extensions/common/manifest_constants.h"
23 #include "sync/api/string_ordinal.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using extensions::Extension
;
27 using extensions::Manifest
;
29 SyncExtensionHelper::ExtensionState::ExtensionState()
30 : enabled_state(ENABLED
), incognito_enabled(false) {}
32 SyncExtensionHelper::ExtensionState::~ExtensionState() {}
34 bool SyncExtensionHelper::ExtensionState::Equals(
35 const SyncExtensionHelper::ExtensionState
&other
) const {
36 return ((enabled_state
== other
.enabled_state
) &&
37 (incognito_enabled
== other
.incognito_enabled
));
41 SyncExtensionHelper
* SyncExtensionHelper::GetInstance() {
42 SyncExtensionHelper
* instance
= Singleton
<SyncExtensionHelper
>::get();
43 instance
->SetupIfNecessary(sync_datatype_helper::test());
47 SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
49 SyncExtensionHelper::~SyncExtensionHelper() {}
51 void SyncExtensionHelper::SetupIfNecessary(SyncTest
* test
) {
55 for (int i
= 0; i
< test
->num_clients(); ++i
) {
56 SetupProfile(test
->GetProfile(i
));
58 SetupProfile(test
->verifier());
60 setup_completed_
= true;
63 std::string
SyncExtensionHelper::InstallExtension(
64 Profile
* profile
, const std::string
& name
, Manifest::Type type
) {
65 scoped_refptr
<Extension
> extension
= GetExtension(profile
, name
, type
);
66 if (!extension
.get()) {
67 NOTREACHED() << "Could not install extension " << name
;
70 profile
->GetExtensionService()
71 ->OnExtensionInstalled(extension
.get(),
72 syncer::StringOrdinal(),
73 false /* no requirement errors */,
74 extensions::NOT_BLACKLISTED
,
75 false /* don't wait for idle to install */);
76 return extension
->id();
79 void SyncExtensionHelper::UninstallExtension(
80 Profile
* profile
, const std::string
& name
) {
81 ExtensionService::UninstallExtensionHelper(
82 profile
->GetExtensionService(),
83 extensions::id_util::GenerateId(name
));
86 std::vector
<std::string
> SyncExtensionHelper::GetInstalledExtensionNames(
87 Profile
* profile
) const {
88 std::vector
<std::string
> names
;
89 ExtensionService
* extension_service
= profile
->GetExtensionService();
91 scoped_ptr
<const extensions::ExtensionSet
> extensions(
92 extension_service
->GenerateInstalledExtensionsSet());
93 for (extensions::ExtensionSet::const_iterator it
= extensions
->begin();
94 it
!= extensions
->end(); ++it
) {
95 names
.push_back((*it
)->name());
101 void SyncExtensionHelper::EnableExtension(Profile
* profile
,
102 const std::string
& name
) {
103 profile
->GetExtensionService()->EnableExtension(
104 extensions::id_util::GenerateId(name
));
107 void SyncExtensionHelper::DisableExtension(Profile
* profile
,
108 const std::string
& name
) {
109 profile
->GetExtensionService()->DisableExtension(
110 extensions::id_util::GenerateId(name
), Extension::DISABLE_USER_ACTION
);
113 bool SyncExtensionHelper::IsExtensionEnabled(
114 Profile
* profile
, const std::string
& name
) const {
115 return profile
->GetExtensionService()->IsExtensionEnabled(
116 extensions::id_util::GenerateId(name
));
119 void SyncExtensionHelper::IncognitoEnableExtension(
120 Profile
* profile
, const std::string
& name
) {
121 extension_util::SetIsIncognitoEnabled(extensions::id_util::GenerateId(name
),
122 profile
->GetExtensionService(), true);
125 void SyncExtensionHelper::IncognitoDisableExtension(
126 Profile
* profile
, const std::string
& name
) {
127 extension_util::SetIsIncognitoEnabled(extensions::id_util::GenerateId(name
),
128 profile
->GetExtensionService(), false);
131 bool SyncExtensionHelper::IsIncognitoEnabled(
132 Profile
* profile
, const std::string
& name
) const {
133 return extension_util::IsIncognitoEnabled(
134 extensions::id_util::GenerateId(name
), profile
->GetExtensionService());
138 bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
139 Profile
* profile
, const std::string
& id
) const {
140 const extensions::PendingExtensionManager
* pending_extension_manager
=
141 profile
->GetExtensionService()->pending_extension_manager();
142 const extensions::PendingExtensionInfo
* info
=
143 pending_extension_manager
->GetById(id
);
146 return info
->is_from_sync();
149 void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile
* profile
) {
150 // TODO(akalin): Mock out the servers that the extensions auto-update
151 // mechanism talk to so as to more closely match what actually happens.
152 // Background networking will need to be re-enabled for extensions tests.
154 // We make a copy here since InstallExtension() removes the
155 // extension from the extensions service's copy.
156 const extensions::PendingExtensionManager
* pending_extension_manager
=
157 profile
->GetExtensionService()->pending_extension_manager();
159 std::list
<std::string
> pending_crx_ids
;
160 pending_extension_manager
->GetPendingIdsForUpdateCheck(&pending_crx_ids
);
162 std::list
<std::string
>::const_iterator iter
;
163 const extensions::PendingExtensionInfo
* info
= NULL
;
164 for (iter
= pending_crx_ids
.begin(); iter
!= pending_crx_ids
.end(); ++iter
) {
165 ASSERT_TRUE((info
= pending_extension_manager
->GetById(*iter
)));
166 if (!info
->is_from_sync())
169 StringMap::const_iterator iter2
= id_to_name_
.find(*iter
);
170 if (iter2
== id_to_name_
.end()) {
171 ADD_FAILURE() << "Could not get name for id " << *iter
172 << " (profile = " << profile
->GetDebugName() << ")";
175 TypeMap::const_iterator iter3
= id_to_type_
.find(*iter
);
176 if (iter3
== id_to_type_
.end()) {
177 ADD_FAILURE() << "Could not get type for id " << *iter
178 << " (profile = " << profile
->GetDebugName() << ")";
180 InstallExtension(profile
, iter2
->second
, iter3
->second
);
184 SyncExtensionHelper::ExtensionStateMap
185 SyncExtensionHelper::GetExtensionStates(Profile
* profile
) {
186 const std::string
& profile_debug_name
= profile
->GetDebugName();
188 ExtensionStateMap extension_state_map
;
190 ExtensionService
* extension_service
= profile
->GetExtensionService();
192 scoped_ptr
<const extensions::ExtensionSet
> extensions(
193 extension_service
->GenerateInstalledExtensionsSet());
194 for (extensions::ExtensionSet::const_iterator it
= extensions
->begin();
195 it
!= extensions
->end(); ++it
) {
196 const std::string
& id
= (*it
)->id();
197 extension_state_map
[id
].enabled_state
=
198 extension_service
->IsExtensionEnabled(id
) ?
199 ExtensionState::ENABLED
:
200 ExtensionState::DISABLED
;
201 extension_state_map
[id
].incognito_enabled
=
202 extension_util::IsIncognitoEnabled(id
, extension_service
);
204 DVLOG(2) << "Extension " << (*it
)->id() << " in profile "
205 << profile_debug_name
<< " is "
206 << (extension_service
->IsExtensionEnabled(id
) ?
207 "enabled" : "disabled");
210 const extensions::PendingExtensionManager
* pending_extension_manager
=
211 extension_service
->pending_extension_manager();
213 std::list
<std::string
> pending_crx_ids
;
214 pending_extension_manager
->GetPendingIdsForUpdateCheck(&pending_crx_ids
);
216 std::list
<std::string
>::const_iterator id
;
217 for (id
= pending_crx_ids
.begin(); id
!= pending_crx_ids
.end(); ++id
) {
218 extension_state_map
[*id
].enabled_state
= ExtensionState::PENDING
;
219 extension_state_map
[*id
].incognito_enabled
=
220 extension_util::IsIncognitoEnabled(*id
, extension_service
);
221 DVLOG(2) << "Extension " << *id
<< " in profile "
222 << profile_debug_name
<< " is pending";
225 return extension_state_map
;
228 bool SyncExtensionHelper::ExtensionStatesMatch(
229 Profile
* profile1
, Profile
* profile2
) {
230 const ExtensionStateMap
& state_map1
= GetExtensionStates(profile1
);
231 const ExtensionStateMap
& state_map2
= GetExtensionStates(profile2
);
232 if (state_map1
.size() != state_map2
.size()) {
233 DVLOG(1) << "Number of extensions for profile " << profile1
->GetDebugName()
234 << " does not match profile " << profile2
->GetDebugName();
238 ExtensionStateMap::const_iterator it1
= state_map1
.begin();
239 ExtensionStateMap::const_iterator it2
= state_map2
.begin();
240 while (it1
!= state_map1
.end()) {
241 if (it1
->first
!= it2
->first
) {
242 DVLOG(1) << "Extensions for profile " << profile1
->GetDebugName()
243 << " do not match profile " << profile2
->GetDebugName();
245 } else if (!it1
->second
.Equals(it2
->second
)) {
246 DVLOG(1) << "Extension states for profile " << profile1
->GetDebugName()
247 << " do not match profile " << profile2
->GetDebugName();
256 void SyncExtensionHelper::SetupProfile(Profile
* profile
) {
257 extensions::ExtensionSystem::Get(profile
)->InitForRegularProfile(true);
258 profile_extensions_
.insert(make_pair(profile
, ExtensionNameMap()));
263 std::string
NameToPublicKey(const std::string
& name
) {
264 std::string public_key
;
266 EXPECT_TRUE(Extension::ProducePEM(name
, &pem
) &&
267 Extension::FormatPEMForFileOutput(pem
, &public_key
,
268 true /* is_public */));
272 // TODO(akalin): Somehow unify this with MakeExtension() in
273 // extension_util_unittest.cc.
274 scoped_refptr
<Extension
> CreateExtension(const base::FilePath
& base_dir
,
275 const std::string
& name
,
276 Manifest::Type type
) {
277 base::DictionaryValue source
;
278 source
.SetString(extensions::manifest_keys::kName
, name
);
279 const std::string
& public_key
= NameToPublicKey(name
);
280 source
.SetString(extensions::manifest_keys::kPublicKey
, public_key
);
281 source
.SetString(extensions::manifest_keys::kVersion
, "0.0.0.0");
283 case Manifest::TYPE_EXTENSION
:
286 case Manifest::TYPE_THEME
:
287 source
.Set(extensions::manifest_keys::kTheme
,
288 new base::DictionaryValue());
290 case Manifest::TYPE_HOSTED_APP
:
291 case Manifest::TYPE_LEGACY_PACKAGED_APP
:
292 source
.Set(extensions::manifest_keys::kApp
, new base::DictionaryValue());
293 source
.SetString(extensions::manifest_keys::kLaunchWebURL
,
294 "http://www.example.com");
296 case Manifest::TYPE_PLATFORM_APP
: {
297 source
.Set(extensions::manifest_keys::kApp
, new base::DictionaryValue());
298 source
.Set(extensions::manifest_keys::kPlatformAppBackground
,
299 new base::DictionaryValue());
300 base::ListValue
* scripts
= new base::ListValue();
301 scripts
->AppendString("main.js");
302 source
.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts
,
310 const base::FilePath sub_dir
= base::FilePath().AppendASCII(name
);
311 base::FilePath extension_dir
;
312 if (!base::PathExists(base_dir
) &&
313 !base::CreateDirectory(base_dir
)) {
317 if (!base::CreateTemporaryDirInDir(base_dir
, sub_dir
.value(),
323 scoped_refptr
<Extension
> extension
=
324 Extension::Create(extension_dir
, Manifest::INTERNAL
, source
,
325 Extension::NO_FLAGS
, &error
);
326 if (!error
.empty()) {
327 ADD_FAILURE() << error
;
330 if (!extension
.get()) {
334 if (extension
->name() != name
) {
335 EXPECT_EQ(name
, extension
->name());
338 if (extension
->GetType() != type
) {
339 EXPECT_EQ(type
, extension
->GetType());
347 scoped_refptr
<Extension
> SyncExtensionHelper::GetExtension(
348 Profile
* profile
, const std::string
& name
, Manifest::Type type
) {
353 ProfileExtensionNameMap::iterator it
= profile_extensions_
.find(profile
);
354 if (it
== profile_extensions_
.end()) {
358 ExtensionNameMap::const_iterator it2
= it
->second
.find(name
);
359 if (it2
!= it
->second
.end()) {
363 scoped_refptr
<Extension
> extension
=
364 CreateExtension(profile
->GetExtensionService()->install_directory(),
366 if (!extension
.get()) {
370 const std::string
& expected_id
= extensions::id_util::GenerateId(name
);
371 if (extension
->id() != expected_id
) {
372 EXPECT_EQ(expected_id
, extension
->id());
375 DVLOG(2) << "created extension with name = "
376 << name
<< ", id = " << expected_id
;
377 (it
->second
)[name
] = extension
;
378 id_to_name_
[expected_id
] = name
;
379 id_to_type_
[expected_id
] = type
;