15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/test/integration/sync_extension_helper.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/extensions/pending_extension_info.h"
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/extensions/pending_extension_manager.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync/test/integration/sync_test.h"
1803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "components/crx_file/id_util.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
21f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "extensions/browser/install_flag.h"
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/uninstall_reason.h"
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension_set.h"
253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "extensions/common/manifest_constants.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/api/string_ordinal.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::ExtensionRegistry;
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Manifest;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper::ExtensionState::ExtensionState()
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : enabled_state(ENABLED), incognito_enabled(false) {}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper::ExtensionState::~ExtensionState() {}
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SyncExtensionHelper::ExtensionState::Equals(
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SyncExtensionHelper::ExtensionState &other) const {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ((enabled_state == other.enabled_state) &&
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (incognito_enabled == other.incognito_enabled));
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper* SyncExtensionHelper::GetInstance() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SyncExtensionHelper* instance = Singleton<SyncExtensionHelper>::get();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance->SetupIfNecessary(sync_datatype_helper::test());
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return instance;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper::SyncExtensionHelper() : setup_completed_(false) {}
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper::~SyncExtensionHelper() {}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SyncExtensionHelper::SetupIfNecessary(SyncTest* test) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (setup_completed_)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < test->num_clients(); ++i) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetupProfile(test->GetProfile(i));
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetupProfile(test->verifier());
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setup_completed_ = true;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string SyncExtensionHelper::InstallExtension(
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Profile* profile, const std::string& name, Manifest::Type type) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension = GetExtension(profile, name, type);
70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!extension.get()) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Could not install extension " << name;
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return std::string();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  extensions::ExtensionSystem::Get(profile)
7523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      ->extension_service()
76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      ->OnExtensionInstalled(extension.get(),
77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                             syncer::StringOrdinal(),
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             extensions::kInstallFlagInstallImmediately);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension->id();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SyncExtensionHelper::UninstallExtension(
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& name) {
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService::UninstallExtensionHelper(
8523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile)->extension_service(),
8603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      crx_file::id_util::GenerateId(name),
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      extensions::UNINSTALL_REASON_SYNC);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<std::string> SyncExtensionHelper::GetInstalledExtensionNames(
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile) const {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> names;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<const extensions::ExtensionSet> extensions(
9523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionRegistry::Get(profile)
9623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->GenerateInstalledExtensionsSet());
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (extensions::ExtensionSet::const_iterator it = extensions->begin();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != extensions->end(); ++it) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    names.push_back((*it)->name());
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return names;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void SyncExtensionHelper::EnableExtension(Profile* profile,
106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                          const std::string& name) {
10723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  extensions::ExtensionSystem::Get(profile)
10823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      ->extension_service()
10903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ->EnableExtension(crx_file::id_util::GenerateId(name));
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void SyncExtensionHelper::DisableExtension(Profile* profile,
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                           const std::string& name) {
11423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  extensions::ExtensionSystem::Get(profile)
11523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      ->extension_service()
11603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ->DisableExtension(crx_file::id_util::GenerateId(name),
11723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                         Extension::DISABLE_USER_ACTION);
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SyncExtensionHelper::IsExtensionEnabled(
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& name) const {
12223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  return extensions::ExtensionSystem::Get(profile)
12323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      ->extension_service()
12403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      ->IsExtensionEnabled(crx_file::id_util::GenerateId(name));
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SyncExtensionHelper::IncognitoEnableExtension(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& name) {
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  extensions::util::SetIsIncognitoEnabled(
13003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      crx_file::id_util::GenerateId(name), profile, true);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SyncExtensionHelper::IncognitoDisableExtension(
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& name) {
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  extensions::util::SetIsIncognitoEnabled(
13603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      crx_file::id_util::GenerateId(name), profile, false);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SyncExtensionHelper::IsIncognitoEnabled(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& name) const {
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return extensions::util::IsIncognitoEnabled(
14203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      crx_file::id_util::GenerateId(name), profile);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SyncExtensionHelper::IsExtensionPendingInstallForSync(
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile, const std::string& id) const {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::PendingExtensionManager* pending_extension_manager =
14923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile)
15023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->extension_service()
15123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->pending_extension_manager();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::PendingExtensionInfo* info =
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pending_extension_manager->GetById(id);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!info)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return info->is_from_sync();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SyncExtensionHelper::InstallExtensionsPendingForSync(Profile* profile) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(akalin): Mock out the servers that the extensions auto-update
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mechanism talk to so as to more closely match what actually happens.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Background networking will need to be re-enabled for extensions tests.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We make a copy here since InstallExtension() removes the
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // extension from the extensions service's copy.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::PendingExtensionManager* pending_extension_manager =
16723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile)
16823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->extension_service()
16923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->pending_extension_manager();
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::list<std::string> pending_crx_ids;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::list<std::string>::const_iterator iter;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::PendingExtensionInfo* info = NULL;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (iter = pending_crx_ids.begin(); iter != pending_crx_ids.end(); ++iter) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE((info = pending_extension_manager->GetById(*iter)));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!info->is_from_sync())
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StringMap::const_iterator iter2 = id_to_name_.find(*iter);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (iter2 == id_to_name_.end()) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ADD_FAILURE() << "Could not get name for id " << *iter
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    << " (profile = " << profile->GetDebugName() << ")";
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TypeMap::const_iterator iter3 = id_to_type_.find(*iter);
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (iter3 == id_to_type_.end()) {
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ADD_FAILURE() << "Could not get type for id " << *iter
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    << " (profile = " << profile->GetDebugName() << ")";
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InstallExtension(profile, iter2->second, iter3->second);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SyncExtensionHelper::ExtensionStateMap
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SyncExtensionHelper::GetExtensionStates(Profile* profile) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& profile_debug_name = profile->GetDebugName();
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionStateMap extension_state_map;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<const extensions::ExtensionSet> extensions(
20323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionRegistry::Get(profile)
20423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)          ->GenerateInstalledExtensionsSet());
20523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
20623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  ExtensionService* extension_service =
20723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      extensions::ExtensionSystem::Get(profile)->extension_service();
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (extensions::ExtensionSet::const_iterator it = extensions->begin();
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != extensions->end(); ++it) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& id = (*it)->id();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_state_map[id].enabled_state =
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extension_service->IsExtensionEnabled(id) ?
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ExtensionState::ENABLED :
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ExtensionState::DISABLED;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_state_map[id].incognito_enabled =
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        extensions::util::IsIncognitoEnabled(id, profile);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(2) << "Extension " << (*it)->id() << " in profile "
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             << profile_debug_name << " is "
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             << (extension_service->IsExtensionEnabled(id) ?
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 "enabled" : "disabled");
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const extensions::PendingExtensionManager* pending_extension_manager =
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_service->pending_extension_manager();
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::list<std::string> pending_crx_ids;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_crx_ids);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::list<std::string>::const_iterator id;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (id = pending_crx_ids.begin(); id != pending_crx_ids.end(); ++id) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_state_map[*id].enabled_state = ExtensionState::PENDING;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_state_map[*id].incognito_enabled =
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        extensions::util::IsIncognitoEnabled(*id, profile);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(2) << "Extension " << *id << " in profile "
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             << profile_debug_name << " is pending";
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension_state_map;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SyncExtensionHelper::ExtensionStatesMatch(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile1, Profile* profile2) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ExtensionStateMap& state_map1 = GetExtensionStates(profile1);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ExtensionStateMap& state_map2 = GetExtensionStates(profile2);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (state_map1.size() != state_map2.size()) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "Number of extensions for profile " << profile1->GetDebugName()
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             << " does not match profile " << profile2->GetDebugName();
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionStateMap::const_iterator it1 = state_map1.begin();
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionStateMap::const_iterator it2 = state_map2.begin();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (it1 != state_map1.end()) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it1->first != it2->first) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DVLOG(1) << "Extensions for profile " << profile1->GetDebugName()
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << " do not match profile " << profile2->GetDebugName();
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!it1->second.Equals(it2->second)) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DVLOG(1) << "Extension states for profile " << profile1->GetDebugName()
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << " do not match profile " << profile2->GetDebugName();
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++it1;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++it2;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SyncExtensionHelper::SetupProfile(Profile* profile) {
271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(true);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  profile_extensions_.insert(make_pair(profile, ExtensionNameMap()));
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string NameToPublicKey(const std::string& name) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string public_key;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string pem;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(Extension::ProducePEM(name, &pem) &&
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              Extension::FormatPEMForFileOutput(pem, &public_key,
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                true /* is_public */));
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return public_key;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(akalin): Somehow unify this with MakeExtension() in
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// extension_util_unittest.cc.
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_refptr<Extension> CreateExtension(const base::FilePath& base_dir,
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const std::string& name,
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         Manifest::Type type) {
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue source;
2923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  source.SetString(extensions::manifest_keys::kName, name);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& public_key = NameToPublicKey(name);
2943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  source.SetString(extensions::manifest_keys::kPublicKey, public_key);
2953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  source.SetString(extensions::manifest_keys::kVersion, "0.0.0.0");
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case Manifest::TYPE_EXTENSION:
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Do nothing.
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case Manifest::TYPE_THEME:
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      source.Set(extensions::manifest_keys::kTheme,
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 new base::DictionaryValue());
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case Manifest::TYPE_HOSTED_APP:
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case Manifest::TYPE_LEGACY_PACKAGED_APP:
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
3073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      source.SetString(extensions::manifest_keys::kLaunchWebURL,
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       "http://www.example.com");
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case Manifest::TYPE_PLATFORM_APP: {
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      source.Set(extensions::manifest_keys::kApp, new base::DictionaryValue());
3123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      source.Set(extensions::manifest_keys::kPlatformAppBackground,
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 new base::DictionaryValue());
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::ListValue* scripts = new base::ListValue();
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      scripts->AppendString("main.js");
3163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      source.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts,
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 scripts);
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ADD_FAILURE();
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NULL;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::FilePath sub_dir = base::FilePath().AppendASCII(name);
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath extension_dir;
3267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!base::PathExists(base_dir) &&
327a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      !base::CreateDirectory(base_dir)) {
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ADD_FAILURE();
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
331a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!base::CreateTemporaryDirInDir(base_dir, sub_dir.value(),
332a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     &extension_dir)) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE();
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string error;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension =
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Extension::Create(extension_dir, Manifest::INTERNAL, source,
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        Extension::NO_FLAGS, &error);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!error.empty()) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE() << error;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
344868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!extension.get()) {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE();
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension->name() != name) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(name, extension->name());
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension->GetType() != type) {
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(type, extension->GetType());
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scoped_refptr<Extension> SyncExtensionHelper::GetExtension(
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Profile* profile, const std::string& name, Manifest::Type type) {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (name.empty()) {
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE();
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileExtensionNameMap::iterator it = profile_extensions_.find(profile);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it == profile_extensions_.end()) {
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE();
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionNameMap::const_iterator it2 = it->second.find(name);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it2 != it->second.end()) {
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return it2->second;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension =
37823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      CreateExtension(extensions::ExtensionSystem::Get(profile)
37923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                          ->extension_service()
38023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                          ->install_directory(),
38123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                      name,
38223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)                      type);
383868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!extension.get()) {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ADD_FAILURE();
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
38703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  const std::string& expected_id = crx_file::id_util::GenerateId(name);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension->id() != expected_id) {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(expected_id, extension->id());
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(2) << "created extension with name = "
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << name << ", id = " << expected_id;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (it->second)[name] = extension;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  id_to_name_[expected_id] = name;
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  id_to_type_[expected_id] = type;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
399