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