sync_extension_helper.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.
4
5#include "chrome/browser/sync/test/integration/sync_extension_helper.h"
6
7#include "base/file_path.h"
8#include "base/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_system.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 "chrome/common/extensions/extension.h"
19#include "chrome/common/extensions/extension_manifest_constants.h"
20#include "sync/api/string_ordinal.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using extensions::Extension;
24
25SyncExtensionHelper::ExtensionState::ExtensionState()
26    : enabled_state(ENABLED), incognito_enabled(false) {}
27
28SyncExtensionHelper::ExtensionState::~ExtensionState() {}
29
30bool SyncExtensionHelper::ExtensionState::Equals(
31    const SyncExtensionHelper::ExtensionState &other) const {
32  return ((enabled_state == other.enabled_state) &&
33          (incognito_enabled == other.incognito_enabled));
34}
35
36// static
37SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
38  SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
39  instance->SetupIfNecessary(sync_datatype_helper::test());
40  return instance;
41}
42
43SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
44
45SyncExtensionHelper::~SyncExtensionHelper() {}
46
47// static
48std::string SyncExtensionHelper::NameToId(const std::string& name) {
49  std::string id;
50  EXPECT_TRUE(Extension::GenerateId(name, &id));
51  return id;
52}
53
54void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) {
55  if (setup_completed_)
56    return;
57
58  for (int i = 0; i < test->num_clients(); ++i) {
59    SetupProfile(test->GetProfile(i));
60  }
61  SetupProfile(test->verifier());
62
63  setup_completed_ = true;
64}
65
66std::string SyncExtensionHelper::InstallExtension(
67    Profile* profile, const std::string& name, Extension::Type type) {
68  scoped_refptr<Extension> extension = GetExtension(profile, name, type);
69  if (!extension.get()) {
70    NOTREACHED() << "Could not install extension " << name;
71    return "";
72  }
73  profile->GetExtensionService()->OnExtensionInstalled(
74      extension, syncer::StringOrdinal(), false /* no requirement errors */,
75      false /* don't wait for idle to install */);
76  return extension->id();
77}
78
79void SyncExtensionHelper::UninstallExtension(
80    Profile* profile, const std::string& name) {
81  ExtensionService::UninstallExtensionHelper(profile->GetExtensionService(),
82                                             NameToId(name));
83}
84
85std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
86    Profile* profile) const {
87  std::vector<std::string> names;
88  ExtensionService* extension_service = profile->GetExtensionService();
89
90  scoped_ptr<const ExtensionSet> extensions(
91      extension_service->GenerateInstalledExtensionsSet());
92  for (ExtensionSet::const_iterator it = extensions->begin();
93       it != extensions->end(); ++it) {
94    names.push_back((*it)->name());
95  }
96
97  return names;
98}
99
100void SyncExtensionHelper::EnableExtension(Profile* profile,
101                                          const std::string& name) {
102  profile->GetExtensionService()->EnableExtension(NameToId(name));
103}
104
105void SyncExtensionHelper::DisableExtension(Profile* profile,
106                                           const std::string& name) {
107  profile->GetExtensionService()->DisableExtension(
108      NameToId(name), Extension::DISABLE_USER_ACTION);
109}
110
111bool SyncExtensionHelper::IsExtensionEnabled(
112    Profile* profile, const std::string& name) const {
113  return profile->GetExtensionService()->IsExtensionEnabled(NameToId(name));
114}
115
116void SyncExtensionHelper::IncognitoEnableExtension(
117    Profile* profile, const std::string& name) {
118  profile->GetExtensionService()->SetIsIncognitoEnabled(NameToId(name), true);
119}
120
121void SyncExtensionHelper::IncognitoDisableExtension(
122    Profile* profile, const std::string& name) {
123  profile->GetExtensionService()->SetIsIncognitoEnabled(NameToId(name), false);
124}
125
126bool SyncExtensionHelper::IsIncognitoEnabled(
127    Profile* profile, const std::string& name) const {
128  return profile->GetExtensionService()->IsIncognitoEnabled(NameToId(name));
129}
130
131
132bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
133    Profile* profile, const std::string& id) const {
134  const extensions::PendingExtensionManager* pending_extension_manager =
135      profile->GetExtensionService()->pending_extension_manager();
136  const extensions::PendingExtensionInfo* info =
137      pending_extension_manager->GetById(id);
138  if (!info)
139    return false;
140  return info->is_from_sync();
141}
142
143void SyncExtensionHelper::InstallExtensionsPendingForSync(
144    Profile* profile, Extension::Type type) {
145  // TODO(akalin): Mock out the servers that the extensions auto-update
146  // mechanism talk to so as to more closely match what actually happens.
147  // Background networking will need to be re-enabled for extensions tests.
148
149  // We make a copy here since InstallExtension() removes the
150  // extension from the extensions service's copy.
151  const extensions::PendingExtensionManager* pending_extension_manager =
152      profile->GetExtensionService()->pending_extension_manager();
153
154  std::list<std::string> pending_crx_ids;
155  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
156
157  std::list<std::string>::const_iterator iter;
158  const extensions::PendingExtensionInfo* info = NULL;
159  for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) {
160    ASSERT_TRUE((info = pending_extension_manager->GetById(*iter)));
161    if (!info->is_from_sync())
162      continue;
163
164    StringMap::const_iterator iter2 = id_to_name_.find(*iter);
165    if (iter2 == id_to_name_.end()) {
166      ADD_FAILURE() << "Could not get name for id " << *iter
167                    << " (profile = " << profile->GetDebugName() << ")";
168      continue;
169    }
170    InstallExtension(profile, iter2->second, type);
171  }
172}
173
174SyncExtensionHelper::ExtensionStateMap
175    SyncExtensionHelper::GetExtensionStates(Profile* profile) {
176  const std::string& profile_debug_name = profile->GetDebugName();
177
178  ExtensionStateMap extension_state_map;
179
180  ExtensionService* extension_service = profile->GetExtensionService();
181
182  scoped_ptr<const ExtensionSet> extensions(
183      extension_service->GenerateInstalledExtensionsSet());
184  for (ExtensionSet::const_iterator it = extensions->begin();
185       it != extensions->end(); ++it) {
186    const std::string& id = (*it)->id();
187    extension_state_map[id].enabled_state =
188        extension_service->IsExtensionEnabled(id) ?
189        ExtensionState::ENABLED :
190        ExtensionState::DISABLED;
191    extension_state_map[id].incognito_enabled =
192        extension_service->IsIncognitoEnabled(id);
193
194    DVLOG(2) << "Extension " << (*it)->id() << " in profile "
195             << profile_debug_name << " is "
196             << (extension_service->IsExtensionEnabled(id) ?
197                 "enabled" : "disabled");
198  }
199
200  const extensions::PendingExtensionManager* pending_extension_manager =
201      extension_service->pending_extension_manager();
202
203  std::list<std::string> pending_crx_ids;
204  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
205
206  std::list<std::string>::const_iterator id;
207  for (id = pending_crx_ids.begin(); id != pending_crx_ids.end(); ++id) {
208    extension_state_map[*id].enabled_state = ExtensionState::PENDING;
209    extension_state_map[*id].incognito_enabled =
210        extension_service->IsIncognitoEnabled(*id);
211    DVLOG(2) << "Extension " << *id << " in profile "
212             << profile_debug_name << " is pending";
213  }
214
215  return extension_state_map;
216}
217
218bool SyncExtensionHelper::ExtensionStatesMatch(
219    Profile* profile1, Profile* profile2) {
220  const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
221  const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
222  if (state_map1.size() != state_map2.size()) {
223    DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
224             << " does not match profile " << profile2->GetDebugName();
225    return false;
226  }
227
228  ExtensionStateMap::const_iterator it1 = state_map1.begin();
229  ExtensionStateMap::const_iterator it2 = state_map2.begin();
230  while (it1 != state_map1.end()) {
231    if (it1->first != it2->first) {
232      DVLOG(1) << "Extensions for profile " << profile1->GetDebugName()
233               << " do not match profile " << profile2->GetDebugName();
234      return false;
235    } else if (!it1->second.Equals(it2->second)) {
236      DVLOG(1) << "Extension states for profile " << profile1->GetDebugName()
237               << " do not match profile " << profile2->GetDebugName();
238      return false;
239    }
240    ++it1;
241    ++it2;
242  }
243  return true;
244}
245
246void SyncExtensionHelper::SetupProfile(Profile* profile) {
247  extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
248  profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
249}
250
251namespace {
252
253std::string NameToPublicKey(const std::string& name) {
254  std::string public_key;
255  std::string pem;
256  EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
257              Extension::FormatPEMForFileOutput(pem, &public_key,
258                                                true /* is_public */));
259  return public_key;
260}
261
262// TODO(akalin): Somehow unify this with MakeExtension() in
263// extension_util_unittest.cc.
264scoped_refptr<Extension> CreateExtension(
265    const FilePath& base_dir, const std::string& name,
266    Extension::Type type) {
267  DictionaryValue source;
268  source.SetString(extension_manifest_keys::kName, name);
269  const std::string& public_key = NameToPublicKey(name);
270  source.SetString(extension_manifest_keys::kPublicKey, public_key);
271  source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
272  switch (type) {
273    case Extension::TYPE_EXTENSION:
274      // Do nothing.
275      break;
276    case Extension::TYPE_THEME:
277      source.Set(extension_manifest_keys::kTheme, new DictionaryValue());
278      break;
279    case Extension::TYPE_HOSTED_APP:
280    case Extension::TYPE_LEGACY_PACKAGED_APP:
281      source.Set(extension_manifest_keys::kApp, new DictionaryValue());
282      source.SetString(extension_manifest_keys::kLaunchWebURL,
283                       "http://www.example.com");
284      break;
285    default:
286      ADD_FAILURE();
287      return NULL;
288  }
289  const FilePath sub_dir = FilePath().AppendASCII(name);
290  FilePath extension_dir;
291  if (!file_util::PathExists(base_dir) &&
292      !file_util::CreateDirectory(base_dir) &&
293      !file_util::CreateTemporaryDirInDir(
294          base_dir, sub_dir.value(), &extension_dir)) {
295    ADD_FAILURE();
296    return NULL;
297  }
298  std::string error;
299  scoped_refptr<Extension> extension =
300      Extension::Create(extension_dir, Extension::INTERNAL, source,
301                        Extension::NO_FLAGS, &error);
302  if (!error.empty()) {
303    ADD_FAILURE() << error;
304    return NULL;
305  }
306  if (!extension.get()) {
307    ADD_FAILURE();
308    return NULL;
309  }
310  if (extension->name() != name) {
311    EXPECT_EQ(name, extension->name());
312    return NULL;
313  }
314  if (extension->GetType() != type) {
315    EXPECT_EQ(type, extension->GetType());
316    return NULL;
317  }
318  return extension;
319}
320
321}  // namespace
322
323scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
324    Profile* profile, const std::string& name,
325    Extension::Type type) {
326  if (name.empty()) {
327    ADD_FAILURE();
328    return NULL;
329  }
330  ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
331  if (it == profile_extensions_.end()) {
332    ADD_FAILURE();
333    return NULL;
334  }
335  ExtensionNameMap::const_iterator it2 = it->second.find(name);
336  if (it2 != it->second.end()) {
337    return it2->second;
338  }
339
340  scoped_refptr<Extension> extension =
341      CreateExtension(profile->GetExtensionService()->install_directory(),
342                      name, type);
343  if (!extension.get()) {
344    ADD_FAILURE();
345    return NULL;
346  }
347  const std::string& expected_id = NameToId(name);
348  if (extension->id() != expected_id) {
349    EXPECT_EQ(expected_id, extension->id());
350    return NULL;
351  }
352  DVLOG(2) << "created extension with name = "
353           << name << ", id = " << expected_id;
354  (it->second)[name] = extension;
355  id_to_name_[expected_id] = name;
356  return extension;
357}
358