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