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