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