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