Split ExtensionUpdater into two smaller classes.

The new ExtensionDownloader takes care of fetching manifests, parsing them,
and downloading crx files. These are then passed to a Delegate.

The ExtensionUpdater is one such Delegate, and does the same as before.

This makes it possible for other components to download extensions given an ID
and update URL; in particular, the retail-mode AppPack for chromeos.

BUG=chromium-os:25463
TEST=Exactly everything about installing/updating extensions works exactly the same. All tests pass.

Review URL: https://chromiumcodereview.appspot.com/9695019

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@127206 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 20c0dbfae..2688231 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/extensions/extension_process_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/history/history_types.h"
 #include "chrome/browser/history/top_sites.h"
 #include "chrome/browser/infobars/infobar_tab_helper.h"
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index df2dc3f..772243d 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -55,8 +55,8 @@
 #include "chrome/browser/extensions/extension_process_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/history/top_sites.h"
 #include "chrome/browser/importer/importer_host.h"
 #include "chrome/browser/importer/importer_list.h"
@@ -6825,7 +6825,7 @@
     return;
   }
 
-  ExtensionUpdater* updater = service->updater();
+  extensions::ExtensionUpdater* updater = service->updater();
   if (!updater) {
     AutomationJSONReply(this, reply_message).SendError(
         "No updater for extensions service.");
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 8f6584a..f35cb0c7 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -22,6 +22,10 @@
 class ExtensionService;
 class SkBitmap;
 
+namespace extensions {
+class ExtensionUpdaterTest;
+}
+
 // This class installs a crx file into a profile.
 //
 // Installing a CRX is a multi-step process, including unpacking the crx,
@@ -185,7 +189,7 @@
   Profile* profile() { return profile_; }
 
  private:
-  friend class ExtensionUpdaterTest;
+  friend class extensions::ExtensionUpdaterTest;
 
   CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak,
                ExtensionInstallUI* client);
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc
index a9e7c23..7841c420 100644
--- a/chrome/browser/extensions/extension_browsertests_misc.cc
+++ b/chrome/browser/extensions/extension_browsertests_misc.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/extensions/extension_process_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc
index 647e37e..ede37af6 100644
--- a/chrome/browser/extensions/extension_management_api.cc
+++ b/chrome/browser/extensions/extension_management_api.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/extensions/extension_event_router.h"
 #include "chrome/browser/extensions/extension_management_api_constants.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc
index 8ef75a6..6d532c1 100644
--- a/chrome/browser/extensions/extension_management_browsertest.cc
+++ b/chrome/browser/extensions/extension_management_browsertest.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/extensions/extension_host.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_test_message_listener.h"
-#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/infobars/infobar_tab_helper.h"
 #include "chrome/browser/prefs/pref_service.h"
 #include "chrome/browser/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index c0252ad9..c0a403e73 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -1770,7 +1770,7 @@
   prefs->RegisterDictionaryPref(kExtensionsPref, PrefService::UNSYNCABLE_PREF);
   prefs->RegisterListPref(kExtensionToolbar, PrefService::UNSYNCABLE_PREF);
   prefs->RegisterIntegerPref(prefs::kExtensionToolbarSize,
-                             -1,
+                             -1,  // default value
                              PrefService::UNSYNCABLE_PREF);
   prefs->RegisterDictionaryPref(kExtensionsBlacklistUpdate,
                                 PrefService::UNSYNCABLE_PREF);
@@ -1781,6 +1781,15 @@
   prefs->RegisterListPref(prefs::kExtensionInstallForceList,
                           PrefService::UNSYNCABLE_PREF);
   prefs->RegisterStringPref(kWebStoreLogin,
-                            std::string() /* default_value */,
+                            std::string(),  // default value
                             PrefService::UNSYNCABLE_PREF);
+  prefs->RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion,
+                            "0",  // default value
+                            PrefService::UNSYNCABLE_PREF);
+  prefs->RegisterInt64Pref(prefs::kLastExtensionsUpdateCheck,
+                           0,  // default value
+                           PrefService::UNSYNCABLE_PREF);
+  prefs->RegisterInt64Pref(prefs::kNextExtensionsUpdateCheck,
+                           0,  // default value
+                           PrefService::UNSYNCABLE_PREF);
 }
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 3c0eb11..af61980d2 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -52,7 +52,6 @@
 #include "chrome/browser/extensions/extension_sorting.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/extensions/extension_web_ui.h"
 #include "chrome/browser/extensions/extension_webnavigation_api.h"
 #include "chrome/browser/extensions/external_extension_provider_impl.h"
@@ -62,6 +61,7 @@
 #include "chrome/browser/extensions/permissions_updater.h"
 #include "chrome/browser/extensions/settings/settings_frontend.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/history/history_extension_api.h"
 #include "chrome/browser/net/chrome_url_request_context.h"
 #include "chrome/browser/prefs/pref_service.h"
@@ -417,11 +417,11 @@
           switches::kExtensionsUpdateFrequency),
           &update_frequency);
     }
-    updater_.reset(new ExtensionUpdater(this,
-                                        extension_prefs,
-                                        profile->GetPrefs(),
-                                        profile,
-                                        update_frequency));
+    updater_.reset(new extensions::ExtensionUpdater(this,
+                                                    extension_prefs,
+                                                    profile->GetPrefs(),
+                                                    profile,
+                                                    update_frequency));
   }
 
   component_loader_.reset(
@@ -1223,7 +1223,7 @@
   return ready_;
 }
 
-ExtensionUpdater* ExtensionService::updater() {
+extensions::ExtensionUpdater* ExtensionService::updater() {
   return updater_.get();
 }
 
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index c090504..4c133d8a 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -59,7 +59,6 @@
 class ExtensionPreferenceEventRouter;
 class ExtensionSyncData;
 class ExtensionToolbarModel;
-class ExtensionUpdater;
 class ExtensionWebNavigationEventRouter;
 class HistoryExtensionEventRouter;
 class GURL;
@@ -75,6 +74,7 @@
 namespace extensions {
 class APIResourceController;
 class ComponentLoader;
+class ExtensionUpdater;
 class RulesRegistryService;
 class SettingsFrontend;
 }
@@ -460,7 +460,7 @@
   }
 
   // Note that this may return NULL if autoupdate is not turned on.
-  ExtensionUpdater* updater();
+  extensions::ExtensionUpdater* updater();
 
   ExtensionToolbarModel* toolbar_model() { return &toolbar_model_; }
 
@@ -753,7 +753,7 @@
   bool ready_;
 
   // Our extension updater, if updates are turned on.
-  scoped_ptr<ExtensionUpdater> updater_;
+  scoped_ptr<extensions::ExtensionUpdater> updater_;
 
   // The model that tracks extensions with BrowserAction buttons.
   ExtensionToolbarModel toolbar_model_;
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 58c161b..65df9afe 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/extensions/extension_sorting.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/extensions/external_extension_provider_impl.h"
 #include "chrome/browser/extensions/external_extension_provider_interface.h"
 #include "chrome/browser/extensions/external_pref_extension_loader.h"
@@ -45,6 +44,7 @@
 #include "chrome/browser/extensions/pending_extension_info.h"
 #include "chrome/browser/extensions/pending_extension_manager.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/plugin_prefs_factory.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/prefs/pref_service_mock_builder.h"
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
deleted file mode 100644
index 8d8483f..0000000
--- a/chrome/browser/extensions/extension_updater.cc
+++ /dev/null
@@ -1,1329 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/extension_updater.h"
-
-#include <algorithm>
-#include <set>
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/memory/scoped_handle.h"
-#include "base/metrics/histogram.h"
-#include "base/rand_util.h"
-#include "base/stl_util.h"
-#include "base/string_number_conversions.h"
-#include "base/string_split.h"
-#include "base/string_util.h"
-#include "base/threading/thread.h"
-#include "base/time.h"
-#include "base/version.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_error_reporter.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/google/google_util.h"
-#include "chrome/browser/metrics/metrics_service.h"
-#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_notification_types.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_utility_messages.h"
-#include "chrome/common/chrome_version_info.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/extension_file_util.h"
-#include "chrome/common/pref_names.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/resource_dispatcher_host.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/common/url_fetcher.h"
-#include "crypto/sha2.h"
-#include "googleurl/src/gurl.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/url_request/url_request_status.h"
-
-#if defined(OS_MACOSX)
-#include "base/sys_string_conversions.h"
-#endif
-
-#define SEND_ACTIVE_PINGS 1
-
-using base::RandDouble;
-using base::RandInt;
-using base::Time;
-using base::TimeDelta;
-using content::BrowserThread;
-using content::UtilityProcessHost;
-using content::UtilityProcessHostClient;
-using prefs::kExtensionBlacklistUpdateVersion;
-using prefs::kLastExtensionsUpdateCheck;
-using prefs::kNextExtensionsUpdateCheck;
-
-// Update AppID for extension blacklist.
-const char ExtensionUpdater::kBlacklistAppID[] = "com.google.crx.blacklist";
-
-const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
-const char kDefaultInstallSource[] = "";
-
-namespace {
-
-// Wait at least 5 minutes after browser startup before we do any checks. If you
-// change this value, make sure to update comments where it is used.
-const int kStartupWaitSeconds = 60 * 5;
-
-// For sanity checking on update frequency - enforced in release mode only.
-static const int kMinUpdateFrequencySeconds = 30;
-static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
-
-// Maximum length of an extension manifest update check url, since it is a GET
-// request. We want to stay under 2K because of proxies, etc.
-static const int kExtensionsManifestMaxURLSize = 2000;
-
-// TODO(skerner): It would be nice to know if the file system failure
-// happens when creating a temp file or when writing to it. Knowing this
-// will require changes to URLFetcher.
-enum FileWriteResult {
-  SUCCESS = 0,
-  CANT_CREATE_OR_WRITE_TEMP_CRX,
-  CANT_READ_CRX_FILE,
-  NUM_FILE_WRITE_RESULTS
-};
-
-// Prototypes allow the functions to be defined in the order they run.
-void CheckThatCRXIsReadable(const FilePath& crx_path);
-void RecordFileUpdateHistogram(FileWriteResult file_write_result);
-
-// Record the result of writing a CRX file. Will be used to understand
-// high failure rates of CRX installs in the field.  If |success| is
-// true, |crx_path| should be set to the path to the CRX file.
-void RecordCRXWriteHistogram(bool success, const FilePath& crx_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (!success) {
-    // We know there was an error writing the file.
-    RecordFileUpdateHistogram(CANT_CREATE_OR_WRITE_TEMP_CRX);
-
-  } else {
-    // Test that the file can be read. Based on histograms in
-    // SandboxExtensionUnpacker, we know that many CRX files
-    // can not be read. Try reading.
-    BrowserThread::PostTask(
-        BrowserThread::FILE, FROM_HERE,
-        base::Bind(&CheckThatCRXIsReadable, crx_path));
-  }
-}
-
-void CheckThatCRXIsReadable(const FilePath& crx_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
-  FileWriteResult file_write_result = SUCCESS;
-
-  // Open the file in the same way
-  // SandboxExtensionUnpacker::ValidateSigniture() will.
-  ScopedStdioHandle file(file_util::OpenFile(crx_path, "rb"));
-  if (!file.get()) {
-    LOG(ERROR) << "Can't read CRX file written for update at path "
-               << crx_path.value().c_str();
-    file_write_result = CANT_READ_CRX_FILE;
-  }
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&RecordFileUpdateHistogram, file_write_result));
-}
-
-void RecordFileUpdateHistogram(FileWriteResult file_write_result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrxAsFile",
-                            file_write_result,
-                            NUM_FILE_WRITE_RESULTS);
-}
-
-}  // namespace
-
-ManifestFetchData::ManifestFetchData(const GURL& update_url)
-    : base_url_(update_url),
-      full_url_(update_url) {
-}
-
-ManifestFetchData::~ManifestFetchData() {}
-
-// The format for request parameters in update checks is:
-//
-//   ?x=EXT1_INFO&x=EXT2_INFO
-//
-// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
-//
-//   id=EXTENSION_ID&v=VERSION&uc
-//
-// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
-// looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
-// ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
-// These values will each be present at most once every 24 hours, and indicate
-// the number of days since the last time it was present in an update check.
-//
-// So for two extensions like:
-//   Extension 1- id:aaaa version:1.1
-//   Extension 2- id:bbbb version:2.0
-//
-// the full update url would be:
-//   http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
-//
-// (Note that '=' is %3D and '&' is %26 when urlencoded.)
-bool ManifestFetchData::AddExtension(std::string id, std::string version,
-                                     const PingData& ping_data,
-                                     const std::string& update_url_data,
-                                     const std::string& install_source) {
-  if (extension_ids_.find(id) != extension_ids_.end()) {
-    NOTREACHED() << "Duplicate extension id " << id;
-    return false;
-  }
-
-  // Compute the string we'd append onto the full_url_, and see if it fits.
-  std::vector<std::string> parts;
-  parts.push_back("id=" + id);
-  parts.push_back("v=" + version);
-  if (!install_source.empty())
-    parts.push_back("installsource=" + install_source);
-  parts.push_back("uc");
-
-  if (!update_url_data.empty()) {
-    // Make sure the update_url_data string is escaped before using it so that
-    // there is no chance of overriding the id or v other parameter value
-    // we place into the x= value.
-    parts.push_back("ap=" + net::EscapeQueryParamValue(update_url_data, true));
-  }
-
-  // Append brand code, rollcall and active ping parameters.
-  if (base_url_.DomainIs("google.com")) {
-#if defined(GOOGLE_CHROME_BUILD)
-    std::string brand;
-    google_util::GetBrand(&brand);
-    if (!brand.empty() && !google_util::IsOrganic(brand))
-      parts.push_back("brand=" + brand);
-#endif
-
-    std::string ping_value;
-    pings_[id] = PingData(0, 0);
-
-    if (ping_data.rollcall_days == kNeverPinged ||
-        ping_data.rollcall_days > 0) {
-      ping_value += "r=" + base::IntToString(ping_data.rollcall_days);
-      pings_[id].rollcall_days = ping_data.rollcall_days;
-    }
-#if SEND_ACTIVE_PINGS
-    if (ping_data.active_days == kNeverPinged || ping_data.active_days > 0) {
-      if (!ping_value.empty())
-        ping_value += "&";
-      ping_value += "a=" + base::IntToString(ping_data.active_days);
-      pings_[id].active_days = ping_data.active_days;
-    }
-#endif  // SEND_ACTIVE_PINGS
-    if (!ping_value.empty())
-      parts.push_back("ping=" + net::EscapeQueryParamValue(ping_value, true));
-  }
-
-  std::string extra = full_url_.has_query() ? "&" : "?";
-  extra += "x=" + net::EscapeQueryParamValue(JoinString(parts, '&'), true);
-
-  // Check against our max url size, exempting the first extension added.
-  int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
-  if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
-    UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
-    return false;
-  }
-  UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
-
-  // We have room so go ahead and add the extension.
-  extension_ids_.insert(id);
-  full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
-  return true;
-}
-
-bool ManifestFetchData::Includes(const std::string& extension_id) const {
-  return extension_ids_.find(extension_id) != extension_ids_.end();
-}
-
-bool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
-  std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
-  if (i == pings_.end())
-    return false;
-  int value = 0;
-  if (type == ROLLCALL)
-    value = i->second.rollcall_days;
-  else if (type == ACTIVE)
-    value = i->second.active_days;
-  else
-    NOTREACHED();
-  return value == kNeverPinged || value > 0;
-}
-
-namespace {
-
-// When we've computed a days value, we want to make sure we don't send a
-// negative value (due to the system clock being set backwards, etc.), since -1
-// is a special sentinel value that means "never pinged", and other negative
-// values don't make sense.
-static int SanitizeDays(int days) {
-  if (days < 0)
-    return 0;
-  return days;
-}
-
-// Calculates the value to use for the ping days parameter.
-static int CalculatePingDays(const Time& last_ping_day) {
-  int days = ManifestFetchData::kNeverPinged;
-  if (!last_ping_day.is_null()) {
-    days = SanitizeDays((Time::Now() - last_ping_day).InDays());
-  }
-  return days;
-}
-
-static int CalculateActivePingDays(const Time& last_active_ping_day,
-                                   bool hasActiveBit) {
-  if (!hasActiveBit)
-    return 0;
-  if (last_active_ping_day.is_null())
-    return ManifestFetchData::kNeverPinged;
-  return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
-}
-
-}  // namespace
-
-ManifestFetchesBuilder::ManifestFetchesBuilder(
-    ExtensionServiceInterface* service,
-    ExtensionPrefs* prefs)
-    : service_(service), prefs_(prefs) {
-  DCHECK(service_);
-  DCHECK(prefs_);
-}
-
-ManifestFetchesBuilder::~ManifestFetchesBuilder() {}
-
-void ManifestFetchesBuilder::AddExtension(const Extension& extension) {
-  // Skip extensions with empty update URLs converted from user
-  // scripts.
-  if (extension.converted_from_user_script() &&
-      extension.update_url().is_empty()) {
-    return;
-  }
-
-  // If the extension updates itself from the gallery, ignore any update URL
-  // data.  At the moment there is no extra data that an extension can
-  // communicate to the the gallery update servers.
-  std::string update_url_data;
-  if (!extension.UpdatesFromGallery())
-    update_url_data = prefs_->GetUpdateUrlData(extension.id());
-
-  AddExtensionData(extension.location(),
-                   extension.id(),
-                   *extension.version(),
-                   extension.GetType(),
-                   extension.update_url(), update_url_data);
-}
-
-void ManifestFetchesBuilder::AddPendingExtension(
-    const std::string& id,
-    Extension::Location install_source,
-    const GURL& update_url) {
-  // Use a zero version to ensure that a pending extension will always
-  // be updated, and thus installed (assuming all extensions have
-  // non-zero versions).
-  Version version("0.0.0.0");
-  DCHECK(version.IsValid());
-
-  AddExtensionData(install_source, id, version,
-                   Extension::TYPE_UNKNOWN, update_url, "");
-}
-
-void ManifestFetchesBuilder::ReportStats() const {
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
-                           url_stats_.extension_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
-                           url_stats_.theme_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
-                           url_stats_.app_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
-                           url_stats_.pending_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
-                           url_stats_.google_url_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
-                           url_stats_.other_url_count);
-  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
-                           url_stats_.no_url_count);
-}
-
-std::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() {
-  std::vector<ManifestFetchData*> fetches;
-  fetches.reserve(fetches_.size());
-  for (std::multimap<GURL, ManifestFetchData*>::iterator it =
-           fetches_.begin(); it != fetches_.end(); ++it) {
-    fetches.push_back(it->second);
-  }
-  fetches_.clear();
-  url_stats_ = URLStats();
-  return fetches;
-}
-
-void ManifestFetchesBuilder::AddExtensionData(
-    Extension::Location location,
-    const std::string& id,
-    const Version& version,
-    Extension::Type extension_type,
-    GURL update_url,
-    const std::string& update_url_data) {
-  if (!Extension::IsAutoUpdateableLocation(location)) {
-    return;
-  }
-
-  // Skip extensions with non-empty invalid update URLs.
-  if (!update_url.is_empty() && !update_url.is_valid()) {
-    LOG(WARNING) << "Extension " << id << " has invalid update url "
-                 << update_url;
-    return;
-  }
-
-  // Skip extensions with empty IDs.
-  if (id.empty()) {
-    LOG(WARNING) << "Found extension with empty ID";
-    return;
-  }
-
-  if (update_url.DomainIs("google.com")) {
-    url_stats_.google_url_count++;
-  } else if (update_url.is_empty()) {
-    url_stats_.no_url_count++;
-    // Fill in default update URL.
-    //
-    // TODO(akalin): Figure out if we should use the HTTPS version.
-    update_url = extension_urls::GetWebstoreUpdateUrl(false);
-  } else {
-    url_stats_.other_url_count++;
-  }
-
-  switch (extension_type) {
-    case Extension::TYPE_THEME:
-      ++url_stats_.theme_count;
-      break;
-    case Extension::TYPE_EXTENSION:
-    case Extension::TYPE_USER_SCRIPT:
-      ++url_stats_.extension_count;
-      break;
-    case Extension::TYPE_HOSTED_APP:
-    case Extension::TYPE_PACKAGED_APP:
-      ++url_stats_.app_count;
-      break;
-    case Extension::TYPE_UNKNOWN:
-    default:
-      ++url_stats_.pending_count;
-      break;
-  }
-
-  std::vector<GURL> update_urls;
-  update_urls.push_back(update_url);
-  // If UMA is enabled, also add to ManifestFetchData for the
-  // webstore update URL.
-  if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
-      MetricsServiceHelper::IsMetricsReportingEnabled()) {
-    update_urls.push_back(extension_urls::GetWebstoreUpdateUrl(false));
-  }
-
-  for (size_t i = 0; i < update_urls.size(); ++i) {
-    DCHECK(!update_urls[i].is_empty());
-    DCHECK(update_urls[i].is_valid());
-
-    std::string install_source = i == 0 ?
-        kDefaultInstallSource : kNotFromWebstoreInstallSource;
-
-    ManifestFetchData* fetch = NULL;
-    std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
-        fetches_.find(update_urls[i]);
-
-    // Find or create a ManifestFetchData to add this extension to.
-    ManifestFetchData::PingData ping_data;
-    ping_data.rollcall_days = CalculatePingDays(prefs_->LastPingDay(id));
-    ping_data.active_days =
-        CalculateActivePingDays(prefs_->LastActivePingDay(id),
-                                prefs_->GetActiveBit(id));
-    while (existing_iter != fetches_.end()) {
-      if (existing_iter->second->AddExtension(
-              id, version.GetString(), ping_data, update_url_data,
-              install_source)) {
-        fetch = existing_iter->second;
-        break;
-      }
-      existing_iter++;
-    }
-    if (!fetch) {
-      fetch = new ManifestFetchData(update_urls[i]);
-      fetches_.insert(std::make_pair(update_urls[i], fetch));
-      bool added = fetch->AddExtension(
-          id, version.GetString(), ping_data, update_url_data, install_source);
-      DCHECK(added);
-    }
-  }
-}
-
-ExtensionUpdater::ExtensionFetch::ExtensionFetch()
-    : id(""),
-      url(),
-      package_hash(""),
-      version("") {}
-
-ExtensionUpdater::ExtensionFetch::ExtensionFetch(const std::string& i,
-                                                 const GURL& u,
-                                                 const std::string& h,
-                                                 const std::string& v)
-    : id(i), url(u), package_hash(h), version(v) {}
-
-ExtensionUpdater::ExtensionFetch::~ExtensionFetch() {}
-
-ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(const std::string& i,
-                                                 const FilePath& p,
-                                                 const GURL& u)
-    : id(i),
-      path(p),
-      download_url(u) {}
-
-ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
-    : id(""),
-      path(),
-      download_url() {}
-
-ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
-
-ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
-                                   ExtensionPrefs* extension_prefs,
-                                   PrefService* prefs,
-                                   Profile* profile,
-                                   int frequency_seconds)
-    : alive_(false),
-      weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
-      service_(service), frequency_seconds_(frequency_seconds),
-      will_check_soon_(false), extension_prefs_(extension_prefs),
-      prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true),
-      crx_install_is_running_(false) {
-  Init();
-}
-
-void ExtensionUpdater::Init() {
-  DCHECK_GE(frequency_seconds_, 5);
-  DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
-#ifdef NDEBUG
-  // In Release mode we enforce that update checks don't happen too often.
-  frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
-#endif
-  frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
-}
-
-ExtensionUpdater::~ExtensionUpdater() {
-  Stop();
-}
-
-static void EnsureInt64PrefRegistered(PrefService* prefs,
-                                      const char name[]) {
-  if (!prefs->FindPreference(name))
-    prefs->RegisterInt64Pref(name, 0, PrefService::UNSYNCABLE_PREF);
-}
-
-static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
-  if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion)) {
-    prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion,
-                              "0",
-                              PrefService::UNSYNCABLE_PREF);
-  }
-}
-
-// The overall goal here is to balance keeping clients up to date while
-// avoiding a thundering herd against update servers.
-TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
-  DCHECK(alive_);
-  // If someone's testing with a quick frequency, just allow it.
-  if (frequency_seconds_ < kStartupWaitSeconds)
-    return TimeDelta::FromSeconds(frequency_seconds_);
-
-  // If we've never scheduled a check before, start at frequency_seconds_.
-  if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
-    return TimeDelta::FromSeconds(frequency_seconds_);
-
-  // If it's been a long time since our last actual check, we want to do one
-  // relatively soon.
-  Time now = Time::Now();
-  Time last = Time::FromInternalValue(prefs_->GetInt64(
-      kLastExtensionsUpdateCheck));
-  int days = (now - last).InDays();
-  if (days >= 30) {
-    // Wait 5-10 minutes.
-    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
-                                          kStartupWaitSeconds * 2));
-  } else if (days >= 14) {
-    // Wait 10-20 minutes.
-    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
-                                          kStartupWaitSeconds * 4));
-  } else if (days >= 3) {
-    // Wait 20-40 minutes.
-    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
-                                          kStartupWaitSeconds * 8));
-  }
-
-  // Read the persisted next check time, and use that if it isn't too soon.
-  // Otherwise pick something random.
-  Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
-      kNextExtensionsUpdateCheck));
-  Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
-  if (saved_next >= earliest) {
-    return saved_next - now;
-  } else {
-    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
-                                          frequency_seconds_));
-  }
-}
-
-void ExtensionUpdater::Start() {
-  DCHECK(!alive_);
-  // If these are NULL, then that means we've been called after Stop()
-  // has been called.
-  DCHECK(service_);
-  DCHECK(extension_prefs_);
-  DCHECK(prefs_);
-  DCHECK(profile_);
-  DCHECK(!weak_ptr_factory_.HasWeakPtrs());
-  alive_ = true;
-  // Make sure our prefs are registered, then schedule the first check.
-  EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
-  EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
-  EnsureBlacklistVersionPrefRegistered(prefs_);
-  ScheduleNextCheck(DetermineFirstCheckDelay());
-}
-
-void ExtensionUpdater::Stop() {
-  weak_ptr_factory_.InvalidateWeakPtrs();
-  alive_ = false;
-  service_ = NULL;
-  extension_prefs_ = NULL;
-  prefs_ = NULL;
-  profile_ = NULL;
-  timer_.Stop();
-  will_check_soon_ = false;
-  manifest_fetcher_.reset();
-  extension_fetcher_.reset();
-  STLDeleteElements(&manifests_pending_);
-  manifests_pending_.clear();
-  extensions_pending_.clear();
-}
-
-void ExtensionUpdater::OnURLFetchComplete(const content::URLFetcher* source) {
-  // Stop() destroys all our URLFetchers, which means we shouldn't be
-  // called after Stop() is called.
-  DCHECK(alive_);
-
-  VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
-
-  if (source == manifest_fetcher_.get()) {
-    std::string data;
-    source->GetResponseAsString(&data);
-    OnManifestFetchComplete(source->GetURL(),
-                            source->GetStatus(),
-                            source->GetResponseCode(),
-                            data);
-  } else if (source == extension_fetcher_.get()) {
-    OnCRXFetchComplete(source,
-                       source->GetURL(),
-                       source->GetStatus(),
-                       source->GetResponseCode());
-  } else {
-    NOTREACHED();
-  }
-  NotifyIfFinished();
-}
-
-// Utility class to handle doing xml parsing in a sandboxed utility process.
-class SafeManifestParser : public UtilityProcessHostClient {
- public:
-  // Takes ownership of |fetch_data|.
-  SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
-                     base::WeakPtr<ExtensionUpdater> updater)
-      : xml_(xml), updater_(updater) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    fetch_data_.reset(fetch_data);
-  }
-
-  // Posts a task over to the IO loop to start the parsing of xml_ in a
-  // utility process.
-  void Start() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    if (!BrowserThread::PostTask(
-            BrowserThread::IO, FROM_HERE,
-            base::Bind(&SafeManifestParser::ParseInSandbox, this))) {
-      NOTREACHED();
-    }
-  }
-
-  // Creates the sandboxed utility process and tells it to start parsing.
-  void ParseInSandbox() {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
-    // TODO(asargent) we shouldn't need to do this branch here - instead
-    // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
-    bool use_utility_process = content::ResourceDispatcherHost::Get() &&
-        !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
-    if (use_utility_process) {
-      UtilityProcessHost* host = UtilityProcessHost::Create(
-          this, BrowserThread::UI);
-      host->EnableZygote();
-      host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml_));
-    } else {
-      UpdateManifest manifest;
-      if (manifest.Parse(xml_)) {
-        if (!BrowserThread::PostTask(
-                BrowserThread::UI, FROM_HERE,
-                base::Bind(
-                    &SafeManifestParser::OnParseUpdateManifestSucceeded, this,
-                    manifest.results()))) {
-          NOTREACHED();
-        }
-      } else {
-        if (!BrowserThread::PostTask(
-                BrowserThread::UI, FROM_HERE,
-                base::Bind(
-                    &SafeManifestParser::OnParseUpdateManifestFailed, this,
-                    manifest.errors()))) {
-          NOTREACHED();
-        }
-      }
-    }
-  }
-
-  // UtilityProcessHostClient
-  virtual bool OnMessageReceived(const IPC::Message& message) {
-    bool handled = true;
-    IPC_BEGIN_MESSAGE_MAP(SafeManifestParser, message)
-      IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
-                          OnParseUpdateManifestSucceeded)
-      IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
-                          OnParseUpdateManifestFailed)
-      IPC_MESSAGE_UNHANDLED(handled = false)
-    IPC_END_MESSAGE_MAP()
-    return handled;
-  }
-
-  void OnParseUpdateManifestSucceeded(
-      const UpdateManifest::Results& results) {
-    VLOG(2) << "parsing manifest succeeded (" << fetch_data_->full_url()
-            << ")";
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    if (!updater_) {
-      return;
-    }
-    updater_->HandleManifestResults(*fetch_data_, &results);
-  }
-
-  void OnParseUpdateManifestFailed(const std::string& error_message) {
-    VLOG(2) << "parsing manifest failed (" << fetch_data_->full_url() << ")";
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    if (!updater_) {
-      return;
-    }
-    LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
-    updater_->HandleManifestResults(*fetch_data_, NULL);
-  }
-
- private:
-  ~SafeManifestParser() {
-    // If we're using UtilityProcessHost, we may not be destroyed on
-    // the UI or IO thread.
-  }
-
-  const std::string xml_;
-
-  // Should be accessed only on UI thread.
-  scoped_ptr<ManifestFetchData> fetch_data_;
-  base::WeakPtr<ExtensionUpdater> updater_;
-};
-
-
-void ExtensionUpdater::OnManifestFetchComplete(
-    const GURL& url,
-    const net::URLRequestStatus& status,
-    int response_code,
-    const std::string& data) {
-  // We want to try parsing the manifest, and if it indicates updates are
-  // available, we want to fire off requests to fetch those updates.
-  if (status.status() == net::URLRequestStatus::SUCCESS &&
-      (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
-    VLOG(2) << "beginning manifest parse for " << url;
-    scoped_refptr<SafeManifestParser> safe_parser(
-        new SafeManifestParser(data, current_manifest_fetch_.release(),
-                               weak_ptr_factory_.GetWeakPtr()));
-    safe_parser->Start();
-  } else {
-    // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
-    VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
-            << "' response code:" << response_code;
-    RemoveFromInProgress(current_manifest_fetch_->extension_ids());
-  }
-  manifest_fetcher_.reset();
-  current_manifest_fetch_.reset();
-
-  // If we have any pending manifest requests, fire off the next one.
-  if (!manifests_pending_.empty()) {
-    ManifestFetchData* manifest_fetch = manifests_pending_.front();
-    manifests_pending_.pop_front();
-    StartUpdateCheck(manifest_fetch);
-  }
-}
-
-void ExtensionUpdater::HandleManifestResults(
-    const ManifestFetchData& fetch_data,
-    const UpdateManifest::Results* results) {
-  DCHECK(alive_);
-
-  // Remove all the ids's from in_progress_ids_ (we will add them back in
-  // below if they actually have updates we need to fetch and install).
-  RemoveFromInProgress(fetch_data.extension_ids());
-
-  if (!results) {
-    NotifyIfFinished();
-    return;
-  }
-
-  // Examine the parsed manifest and kick off fetches of any new crx files.
-  std::vector<int> updates = DetermineUpdates(fetch_data, *results);
-  for (size_t i = 0; i < updates.size(); i++) {
-    const UpdateManifest::Result* update = &(results->list.at(updates[i]));
-    const std::string& id = update->extension_id;
-    in_progress_ids_.insert(id);
-    if (id != std::string(kBlacklistAppID)) {
-      NotifyUpdateFound(update->extension_id);
-    } else {
-      // The URL of the blacklist file is returned by the server and we need to
-      // be sure that we continue to be able to reliably detect whether a URL
-      // references a blacklist file.
-      DCHECK(extension_urls::IsBlacklistUpdateUrl(update->crx_url))
-          << update->crx_url;
-    }
-    FetchUpdatedExtension(update->extension_id, update->crx_url,
-        update->package_hash, update->version);
-  }
-
-  // If the manifest response included a <daystart> element, we want to save
-  // that value for any extensions which had sent a ping in the request.
-  if (fetch_data.base_url().DomainIs("google.com") &&
-      results->daystart_elapsed_seconds >= 0) {
-    Time daystart =
-      Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
-
-    const std::set<std::string>& extension_ids = fetch_data.extension_ids();
-    std::set<std::string>::const_iterator i;
-    for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
-      if (fetch_data.DidPing(*i, ManifestFetchData::ROLLCALL)) {
-        if (*i == kBlacklistAppID) {
-          extension_prefs_->SetBlacklistLastPingDay(daystart);
-        } else if (service_->GetExtensionById(*i, true) != NULL) {
-          extension_prefs_->SetLastPingDay(*i, daystart);
-        }
-      }
-      if (extension_prefs_->GetActiveBit(*i)) {
-        extension_prefs_->SetActiveBit(*i, false);
-        extension_prefs_->SetLastActivePingDay(*i, daystart);
-      }
-    }
-  }
-  NotifyIfFinished();
-}
-
-void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
-  DCHECK(alive_);
-  // Verify sha256 hash value.
-  char sha256_hash_value[crypto::kSHA256Length];
-  crypto::SHA256HashString(data, sha256_hash_value, crypto::kSHA256Length);
-  std::string hash_in_hex = base::HexEncode(sha256_hash_value,
-                                            crypto::kSHA256Length);
-
-  if (current_extension_fetch_.package_hash != hash_in_hex) {
-    NOTREACHED() << "Fetched blacklist checksum is not as expected. "
-      << "Expected: " << current_extension_fetch_.package_hash
-      << " Actual: " << hash_in_hex;
-    return;
-  }
-  std::vector<std::string> blacklist;
-  base::SplitString(data, '\n', &blacklist);
-
-  // Tell ExtensionService to update prefs.
-  service_->UpdateExtensionBlacklist(blacklist);
-
-  // Update the pref value for blacklist version
-  prefs_->SetString(kExtensionBlacklistUpdateVersion,
-                    current_extension_fetch_.version);
-}
-
-void ExtensionUpdater::OnCRXFetchComplete(
-    const content::URLFetcher* source,
-    const GURL& url,
-    const net::URLRequestStatus& status,
-    int response_code) {
-
-  base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
-  if (source->FileErrorOccurred(&error_code)) {
-    LOG(ERROR) << "Failed to write update CRX with id "
-               << current_extension_fetch_.id << ". "
-               << "Error code is "<< error_code;
-
-    RecordCRXWriteHistogram(false, FilePath());
-    OnCRXFileWriteError(current_extension_fetch_.id);
-
-  } else if (status.status() == net::URLRequestStatus::SUCCESS &&
-      (response_code == 200 || url.SchemeIsFile())) {
-    if (current_extension_fetch_.id == kBlacklistAppID) {
-      std::string data;
-      source->GetResponseAsString(&data);
-      ProcessBlacklist(data);
-      in_progress_ids_.erase(current_extension_fetch_.id);
-    } else {
-      FilePath crx_path;
-      // Take ownership of the file at |crx_path|.
-      CHECK(source->GetResponseAsFilePath(true, &crx_path));
-      RecordCRXWriteHistogram(true, crx_path);
-      OnCRXFileWritten(current_extension_fetch_.id, crx_path, url);
-    }
-  } else {
-    // TODO(asargent) do things like exponential backoff, handling
-    // 503 Service Unavailable / Retry-After headers, etc. here.
-    // (http://crbug.com/12546).
-    VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
-            << "' response code:" << response_code;
-  }
-  extension_fetcher_.reset();
-  current_extension_fetch_ = ExtensionFetch();
-
-  // If there are any pending downloads left, start the next one.
-  if (!extensions_pending_.empty()) {
-    ExtensionFetch next = extensions_pending_.front();
-    extensions_pending_.pop_front();
-    FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
-  }
-}
-
-void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
-                                        const FilePath& path,
-                                        const GURL& download_url) {
-  DCHECK(alive_);
-
-  VLOG(2) << download_url << " written to " << path.value();
-
-  FetchedCRXFile fetched(id, path, download_url);
-  fetched_crx_files_.push(fetched);
-
-  MaybeInstallCRXFile();
-}
-
-bool ExtensionUpdater::MaybeInstallCRXFile() {
-  if (crx_install_is_running_)
-    return false;
-
-  while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
-    const FetchedCRXFile& crx_file = fetched_crx_files_.top();
-
-    VLOG(2) << "updating " << crx_file.id << " with " << crx_file.path.value();
-
-    // The ExtensionService is now responsible for cleaning up the temp file
-    // at |extension_file.path|.
-    CrxInstaller* installer = NULL;
-    if (service_->UpdateExtension(crx_file.id,
-                                  crx_file.path,
-                                  crx_file.download_url,
-                                  &installer)) {
-      crx_install_is_running_ = true;
-
-      // Source parameter ensures that we only see the completion event for the
-      // the installer we started.
-      registrar_.Add(this,
-                     chrome::NOTIFICATION_CRX_INSTALLER_DONE,
-                     content::Source<CrxInstaller>(installer));
-    }
-    in_progress_ids_.erase(crx_file.id);
-    fetched_crx_files_.pop();
-  }
-
-  // If an updater is running, it was started above.
-  return crx_install_is_running_;
-}
-
-void ExtensionUpdater::Observe(int type,
-                               const content::NotificationSource& source,
-                               const content::NotificationDetails& details) {
-  DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
-
-  // No need to listen for CRX_INSTALLER_DONE anymore.
-  registrar_.Remove(this,
-                    chrome::NOTIFICATION_CRX_INSTALLER_DONE,
-                    source);
-  crx_install_is_running_ = false;
-  // If any files are available to update, start one.
-  MaybeInstallCRXFile();
-}
-
-void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
-  DCHECK(alive_);
-  in_progress_ids_.erase(id);
-}
-
-void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
-  DCHECK(alive_);
-  DCHECK(!timer_.IsRunning());
-  DCHECK(target_delay >= TimeDelta::FromSeconds(1));
-
-  // Add +/- 10% random jitter.
-  double delay_ms = target_delay.InMillisecondsF();
-  double jitter_factor = (RandDouble() * .2) - 0.1;
-  delay_ms += delay_ms * jitter_factor;
-  TimeDelta actual_delay = TimeDelta::FromMilliseconds(
-      static_cast<int64>(delay_ms));
-
-  // Save the time of next check.
-  Time next = Time::Now() + actual_delay;
-  prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
-
-  timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
-}
-
-void ExtensionUpdater::TimerFired() {
-  DCHECK(alive_);
-  CheckNow();
-
-  // If the user has overridden the update frequency, don't bother reporting
-  // this.
-  if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
-    Time last = Time::FromInternalValue(prefs_->GetInt64(
-        kLastExtensionsUpdateCheck));
-    if (last.ToInternalValue() != 0) {
-      // Use counts rather than time so we can use minutes rather than millis.
-      UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
-          (Time::Now() - last).InMinutes(),
-          base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
-          base::TimeDelta::FromDays(40).InMinutes(),
-          50);  // 50 buckets seems to be the default.
-    }
-  }
-
-  // Save the last check time, and schedule the next check.
-  int64 now = Time::Now().ToInternalValue();
-  prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
-  ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
-}
-
-void ExtensionUpdater::CheckSoon() {
-  DCHECK(alive_);
-  if (will_check_soon_) {
-    return;
-  }
-  if (BrowserThread::PostTask(
-          BrowserThread::UI, FROM_HERE,
-          base::Bind(&ExtensionUpdater::DoCheckSoon,
-                     weak_ptr_factory_.GetWeakPtr()))) {
-    will_check_soon_ = true;
-  } else {
-    NOTREACHED();
-  }
-}
-
-bool ExtensionUpdater::WillCheckSoon() const {
-  return will_check_soon_;
-}
-
-void ExtensionUpdater::DoCheckSoon() {
-  DCHECK(will_check_soon_);
-  CheckNow();
-  will_check_soon_ = false;
-}
-
-void ExtensionUpdater::CheckNow() {
-  VLOG(2) << "Starting update check";
-  DCHECK(alive_);
-  NotifyStarted();
-  ManifestFetchesBuilder fetches_builder(service_, extension_prefs_);
-
-  // Add fetch records for extensions that should be fetched by an update URL.
-  // These extensions are not yet installed.  They come from group policy
-  // and external install sources.
-  const PendingExtensionManager* pending_extension_manager =
-      service_->pending_extension_manager();
-
-  std::set<std::string> pending_ids;
-  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
-
-  std::set<std::string>::const_iterator iter;
-  for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
-    PendingExtensionInfo info;
-    bool found_id = pending_extension_manager->GetById(*iter, &info);
-    DCHECK(found_id);
-    if (!found_id)
-      continue;
-
-    fetches_builder.AddPendingExtension(
-        *iter,
-        info.install_source(),
-        info.update_url());
-  }
-
-  // Add fetch records for extensions that are installed and have an
-  // update URL.
-  const ExtensionSet* extensions = service_->extensions();
-  for (ExtensionSet::const_iterator iter = extensions->begin();
-       iter != extensions->end(); ++iter) {
-    // An extension might be overwritten by policy, and have its update url
-    // changed. Make sure existing extensions aren't fetched again, if a
-    // pending fetch for an extension with the same id already exists.
-    if (!ContainsKey(pending_ids, (*iter)->id())) {
-      fetches_builder.AddExtension(**iter);
-    }
-  }
-
-  fetches_builder.ReportStats();
-
-  std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches());
-
-  // Start a fetch of the blacklist if needed.
-  if (blacklist_checks_enabled_) {
-    // Note: it is very important that we use  the https version of the update
-    // url here to avoid DNS hijacking of the blacklist, which is not validated
-    // by a public key signature like .crx files are.
-    ManifestFetchData* blacklist_fetch =
-        new ManifestFetchData(extension_urls::GetWebstoreUpdateUrl(true));
-    std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
-    ManifestFetchData::PingData ping_data;
-    ping_data.rollcall_days =
-        CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
-    blacklist_fetch->AddExtension(
-        kBlacklistAppID, version, ping_data, "", kDefaultInstallSource);
-    StartUpdateCheck(blacklist_fetch);
-  }
-
-  // Now start fetching regular extension updates
-  for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin();
-       it != fetches.end(); ++it) {
-    // StartUpdateCheck makes sure the url isn't already downloading or
-    // scheduled, so we don't need to check before calling it. Ownership of
-    // fetch is transferred here.
-    StartUpdateCheck(*it);
-  }
-  // We don't want to use fetches after this since StartUpdateCheck()
-  // takes ownership of its argument.
-  fetches.clear();
-
-  NotifyIfFinished();
-}
-
-bool ExtensionUpdater::GetExistingVersion(const std::string& id,
-                                          std::string* version) {
-  DCHECK(alive_);
-  if (id == kBlacklistAppID) {
-    *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
-    return true;
-  }
-  const Extension* extension = service_->GetExtensionById(id, false);
-  if (!extension) {
-    return false;
-  }
-  *version = extension->version()->GetString();
-  return true;
-}
-
-std::vector<int> ExtensionUpdater::DetermineUpdates(
-    const ManifestFetchData& fetch_data,
-    const UpdateManifest::Results& possible_updates) {
-  DCHECK(alive_);
-  std::vector<int> result;
-
-  // This will only be valid if one of possible_updates specifies
-  // browser_min_version.
-  Version browser_version;
-  PendingExtensionManager* pending_extension_manager =
-      service_->pending_extension_manager();
-
-  for (size_t i = 0; i < possible_updates.list.size(); i++) {
-    const UpdateManifest::Result* update = &possible_updates.list[i];
-
-    if (!fetch_data.Includes(update->extension_id)) {
-      VLOG(2) << "Ignoring " << update->extension_id << " from this manifest";
-      continue;
-    }
-
-    if (VLOG_IS_ON(2)) {
-      if (update->version.empty())
-        VLOG(2) << "manifest indicates " << update->extension_id
-                << " has no update";
-      else
-        VLOG(2) << "manifest indicates " << update->extension_id
-                << " latest version is '" << update->version << "'";
-    }
-
-    if (!pending_extension_manager->IsIdPending(update->extension_id)) {
-      // If we're not installing pending extension, and the update
-      // version is the same or older than what's already installed,
-      // we don't want it.
-      std::string version;
-      if (!GetExistingVersion(update->extension_id, &version)) {
-        VLOG(2) << update->extension_id << " is not installed";
-        continue;
-      }
-
-      VLOG(2) << update->extension_id << " is at '" << version << "'";
-
-      Version existing_version(version);
-      Version update_version(update->version);
-
-      if (!update_version.IsValid() ||
-          update_version.CompareTo(existing_version) <= 0) {
-        continue;
-      }
-    }
-
-    // If the update specifies a browser minimum version, do we qualify?
-    if (update->browser_min_version.length() > 0) {
-      // First determine the browser version if we haven't already.
-      if (!browser_version.IsValid()) {
-        chrome::VersionInfo version_info;
-        if (version_info.is_valid())
-          browser_version = Version(version_info.Version());
-      }
-      Version browser_min_version(update->browser_min_version);
-      if (browser_version.IsValid() && browser_min_version.IsValid() &&
-          browser_min_version.CompareTo(browser_version) > 0) {
-        // TODO(asargent) - We may want this to show up in the extensions UI
-        // eventually. (http://crbug.com/12547).
-        LOG(WARNING) << "Updated version of extension " << update->extension_id
-                     << " available, but requires chrome version "
-                     << update->browser_min_version;
-        continue;
-      }
-    }
-    VLOG(2) << "will try to update " << update->extension_id;
-    result.push_back(i);
-  }
-  return result;
-}
-
-void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
-  AddToInProgress(fetch_data->extension_ids());
-
-  scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableBackgroundNetworking))
-    return;
-
-  std::deque<ManifestFetchData*>::const_iterator i;
-  for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
-    if (fetch_data->full_url() == (*i)->full_url()) {
-      // This url is already scheduled to be fetched.
-      return;
-    }
-  }
-
-  if (manifest_fetcher_.get() != NULL) {
-    if (manifest_fetcher_->GetURL() != fetch_data->full_url()) {
-      manifests_pending_.push_back(scoped_fetch_data.release());
-    }
-  } else {
-    UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
-        fetch_data->full_url().possibly_invalid_spec().length());
-
-    if (VLOG_IS_ON(2)) {
-      const std::set<std::string>& id_set = fetch_data->extension_ids();
-      std::vector<std::string> id_vector(id_set.begin(), id_set.end());
-      std::string id_list = JoinString(id_vector, ',');
-      VLOG(2) << "Fetching " << fetch_data->full_url() << " for "
-              << id_list;
-    }
-
-    current_manifest_fetch_.swap(scoped_fetch_data);
-    manifest_fetcher_.reset(content::URLFetcher::Create(
-        kManifestFetcherId, fetch_data->full_url(), content::URLFetcher::GET,
-        this));
-    manifest_fetcher_->SetRequestContext(profile_->GetRequestContext());
-    manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                                    net::LOAD_DO_NOT_SAVE_COOKIES |
-                                    net::LOAD_DISABLE_CACHE);
-    manifest_fetcher_->Start();
-  }
-}
-
-void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
-                                             const GURL& url,
-                                             const std::string& hash,
-                                             const std::string& version) {
-  for (std::deque<ExtensionFetch>::const_iterator iter =
-           extensions_pending_.begin();
-       iter != extensions_pending_.end(); ++iter) {
-    if (iter->id == id || iter->url == url) {
-      return;  // already scheduled
-    }
-  }
-
-  if (extension_fetcher_.get() != NULL) {
-    if (extension_fetcher_->GetURL() != url) {
-      extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
-    }
-  } else {
-    extension_fetcher_.reset(content::URLFetcher::Create(
-        kExtensionFetcherId, url, content::URLFetcher::GET, this));
-    extension_fetcher_->SetRequestContext(
-        profile_->GetRequestContext());
-    extension_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                                     net::LOAD_DO_NOT_SAVE_COOKIES |
-                                     net::LOAD_DISABLE_CACHE);
-    // Download CRX files to a temp file. The blacklist is small and will be
-    // processed in memory, so it is fetched into a string.
-    if (id != ExtensionUpdater::kBlacklistAppID) {
-      extension_fetcher_->SaveResponseToTemporaryFile(
-          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
-    }
-
-    VLOG(2) << "Starting fetch of " << url << " for " << id;
-
-    extension_fetcher_->Start();
-    current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
-  }
-}
-
-void ExtensionUpdater::NotifyStarted() {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
-      content::Source<Profile>(profile_),
-      content::NotificationService::NoDetails());
-}
-
-void ExtensionUpdater::NotifyUpdateFound(const std::string& extension_id) {
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
-      content::Source<Profile>(profile_),
-      content::Details<const std::string>(&extension_id));
-}
-
-void ExtensionUpdater::NotifyIfFinished() {
-  if (in_progress_ids_.empty()) {
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
-        content::Source<Profile>(profile_),
-        content::NotificationService::NoDetails());
-    VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
-  }
-}
-
-void ExtensionUpdater::AddToInProgress(const std::set<std::string>& ids) {
-  std::set<std::string>::const_iterator i;
-  for (i = ids.begin(); i != ids.end(); ++i)
-    in_progress_ids_.insert(*i);
-}
-
-void ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) {
-  std::set<std::string>::const_iterator i;
-  for (i = ids.begin(); i != ids.end(); ++i)
-    in_progress_ids_.erase(*i);
-}
diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h
deleted file mode 100644
index d61438d..0000000
--- a/chrome/browser/extensions/extension_updater.h
+++ /dev/null
@@ -1,397 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
-#pragma once
-
-#include <deque>
-#include <map>
-#include <set>
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_temp_dir.h"
-#include "base/time.h"
-#include "base/timer.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/common/extensions/update_manifest.h"
-#include "content/public/common/url_fetcher_delegate.h"
-#include "googleurl/src/gurl.h"
-
-class Extension;
-class ExtensionPrefs;
-class ExtensionUpdaterTest;
-class PrefService;
-class Profile;
-class SafeManifestParser;
-
-namespace net {
-class URLRequestStatus;
-}
-
-// To save on server resources we can request updates for multiple extensions
-// in one manifest check. This class helps us keep track of the id's for a
-// given fetch, building up the actual URL, and what if anything to include
-// in the ping parameter.
-class ManifestFetchData {
- public:
-  static const int kNeverPinged = -1;
-
-  // Each ping type is sent at most once per day.
-  enum PingType {
-    // Used for counting total installs of an extension/app/theme.
-    ROLLCALL,
-
-    // Used for counting number of active users of an app, where "active" means
-    // the app was launched at least once since the last active ping.
-    ACTIVE
-  };
-
-  struct PingData {
-    // The number of days it's been since our last rollcall or active ping,
-    // respectively. These are calculated based on the start of day from the
-    // server's perspective.
-    int rollcall_days;
-    int active_days;
-
-    PingData() : rollcall_days(0), active_days(0) {}
-    PingData(int rollcall, int active)
-        : rollcall_days(rollcall), active_days(active) {}
-  };
-
-  explicit ManifestFetchData(const GURL& update_url);
-  ~ManifestFetchData();
-
-  // Returns true if this extension information was successfully added. If the
-  // return value is false it means the full_url would have become too long, and
-  // this ManifestFetchData object remains unchanged.
-  bool AddExtension(std::string id, std::string version,
-                    const PingData& ping_data,
-                    const std::string& update_url_data,
-                    const std::string& install_source);
-
-  const GURL& base_url() const { return base_url_; }
-  const GURL& full_url() const { return full_url_; }
-  int extension_count() { return extension_ids_.size(); }
-  const std::set<std::string>& extension_ids() const { return extension_ids_; }
-
-  // Returns true if the given id is included in this manifest fetch.
-  bool Includes(const std::string& extension_id) const;
-
-  // Returns true if a ping parameter for |type| was added to full_url for this
-  // extension id.
-  bool DidPing(std::string extension_id, PingType type) const;
-
- private:
-  // The set of extension id's for this ManifestFetchData.
-  std::set<std::string> extension_ids_;
-
-  // The set of ping data we actually sent.
-  std::map<std::string, PingData> pings_;
-
-  // The base update url without any arguments added.
-  GURL base_url_;
-
-  // The base update url plus arguments indicating the id, version, etc.
-  // information about each extension.
-  GURL full_url_;
-
-  DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
-};
-
-// A class for building a set of ManifestFetchData objects from
-// extensions and pending extensions.
-class ManifestFetchesBuilder {
- public:
-  ManifestFetchesBuilder(ExtensionServiceInterface* service,
-                         ExtensionPrefs* prefs);
-  ~ManifestFetchesBuilder();
-
-  void AddExtension(const Extension& extension);
-
-  void AddPendingExtension(const std::string& id,
-                           Extension::Location install_source,
-                           const GURL& update_url);
-
-  // Adds all recorded stats taken so far to histogram counts.
-  void ReportStats() const;
-
-  // Caller takes ownership of the returned ManifestFetchData
-  // objects.  Clears all recorded stats.
-  std::vector<ManifestFetchData*> GetFetches();
-
- private:
-  struct URLStats {
-    URLStats()
-        : no_url_count(0),
-          google_url_count(0),
-          other_url_count(0),
-          extension_count(0),
-          theme_count(0),
-          app_count(0),
-          pending_count(0) {}
-
-    int no_url_count, google_url_count, other_url_count;
-    int extension_count, theme_count, app_count, pending_count;
-  };
-
-  void AddExtensionData(Extension::Location location,
-                        const std::string& id,
-                        const Version& version,
-                        Extension::Type extension_type,
-                        GURL update_url,
-                        const std::string& update_url_data);
-  ExtensionServiceInterface* const service_;
-  ExtensionPrefs* const prefs_;
-
-  // List of data on fetches we're going to do. We limit the number of
-  // extensions grouped together in one batch to avoid running into the limits
-  // on the length of http GET requests, so there might be multiple
-  // ManifestFetchData* objects with the same base_url.
-  std::multimap<GURL, ManifestFetchData*> fetches_;
-
-  URLStats url_stats_;
-
-  DISALLOW_COPY_AND_ASSIGN(ManifestFetchesBuilder);
-};
-
-// A class for doing auto-updates of installed Extensions. Used like this:
-//
-// ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service,
-//                                                  pref_service,
-//                                                  update_frequency_secs);
-// updater->Start();
-// ....
-// updater->Stop();
-class ExtensionUpdater : public content::URLFetcherDelegate,
-                         public content::NotificationObserver {
- public:
-  // Holds a pointer to the passed |service|, using it for querying installed
-  // extensions and installing updated ones. The |frequency_seconds| parameter
-  // controls how often update checks are scheduled.
-  ExtensionUpdater(ExtensionServiceInterface* service,
-                   ExtensionPrefs* extension_prefs,
-                   PrefService* prefs,
-                   Profile* profile,
-                   int frequency_seconds);
-
-  virtual ~ExtensionUpdater();
-
-  // Starts the updater running.  Should be called at most once.
-  void Start();
-
-  // Stops the updater running, cancelling any outstanding update manifest and
-  // crx downloads. Does not cancel any in-progress installs.
-  void Stop();
-
-  // Posts a task to do an update check.  Does nothing if there is
-  // already a pending task that has not yet run.
-  void CheckSoon();
-
-  // Starts an update check right now, instead of waiting for the next
-  // regularly scheduled check or a pending check from CheckSoon().
-  void CheckNow();
-
-  // Set blacklist checks on or off.
-  void set_blacklist_checks_enabled(bool enabled) {
-    blacklist_checks_enabled_ = enabled;
-  }
-
-  // Returns true iff CheckSoon() has been called but the update check
-  // hasn't been performed yet.  This is used mostly by tests; calling
-  // code should just call CheckSoon().
-  bool WillCheckSoon() const;
-
- private:
-  friend class ExtensionUpdaterTest;
-  friend class ExtensionUpdaterFileHandler;
-  friend class SafeManifestParser;
-
-  // We need to keep track of some information associated with a url
-  // when doing a fetch.
-  struct ExtensionFetch {
-    ExtensionFetch();
-    ExtensionFetch(const std::string& i, const GURL& u,
-                   const std::string& h, const std::string& v);
-    ~ExtensionFetch();
-
-    std::string id;
-    GURL url;
-    std::string package_hash;
-    std::string version;
-  };
-
-  // FetchedCRXFile holds information about a CRX file we fetched to disk,
-  // but have not yet installed.
-  struct FetchedCRXFile {
-    FetchedCRXFile();
-    FetchedCRXFile(const std::string& id,
-                   const FilePath& path,
-                   const GURL& download_url);
-    ~FetchedCRXFile();
-
-    std::string id;
-    FilePath path;
-    GURL download_url;
-  };
-
-  // These are needed for unit testing, to help identify the correct mock
-  // URLFetcher objects.
-  static const int kManifestFetcherId = 1;
-  static const int kExtensionFetcherId = 2;
-
-  static const char kBlacklistAppID[];
-
-  // Does common work from constructors.
-  void Init();
-
-  // Computes when to schedule the first update check.
-  base::TimeDelta DetermineFirstCheckDelay();
-
-  // content::URLFetcherDelegate interface.
-  virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
-
-  // These do the actual work when a URL fetch completes.
-  virtual void OnManifestFetchComplete(const GURL& url,
-                                       const net::URLRequestStatus& status,
-                                       int response_code,
-                                       const std::string& data);
-
-  virtual void OnCRXFetchComplete(const content::URLFetcher* source,
-                                  const GURL& url,
-                                  const net::URLRequestStatus& status,
-                                  int response_code);
-
-  // Called when a crx file has been written into a temp file, and is ready
-  // to be installed.
-  void OnCRXFileWritten(const std::string& id,
-                        const FilePath& path,
-                        const GURL& download_url);
-
-  // Called when we encountered an error writing a crx file to a temp file.
-  void OnCRXFileWriteError(const std::string& id);
-
-  // Verifies downloaded blacklist. Based on the blacklist, calls extension
-  // service to unload blacklisted extensions and update pref.
-  void ProcessBlacklist(const std::string& data);
-
-  // Sets the timer to call TimerFired after roughly |target_delay| from now.
-  // To help spread load evenly on servers, this method adds some random
-  // jitter. It also saves the scheduled time so it can be reloaded on
-  // browser restart.
-  void ScheduleNextCheck(const base::TimeDelta& target_delay);
-
-  // BaseTimer::ReceiverMethod callback.
-  void TimerFired();
-
-  // Posted by CheckSoon().
-  void DoCheckSoon();
-
-  // Begins an update check. Takes ownership of |fetch_data|.
-  void StartUpdateCheck(ManifestFetchData* fetch_data);
-
-  // Begins (or queues up) download of an updated extension.
-  void FetchUpdatedExtension(const std::string& id, const GURL& url,
-    const std::string& hash, const std::string& version);
-
-  // Once a manifest is parsed, this starts fetches of any relevant crx files.
-  // If |results| is null, it means something went wrong when parsing it.
-  void HandleManifestResults(const ManifestFetchData& fetch_data,
-                             const UpdateManifest::Results* results);
-
-  // Determines the version of an existing extension.
-  // Returns true on success and false on failures.
-  bool GetExistingVersion(const std::string& id, std::string* version);
-
-  // Given a list of potential updates, returns the indices of the ones that are
-  // applicable (are actually a new version, etc.) in |result|.
-  std::vector<int> DetermineUpdates(const ManifestFetchData& fetch_data,
-      const UpdateManifest::Results& possible_updates);
-
-  // Send a notification that update checks are starting.
-  void NotifyStarted();
-
-  // Send a notification that an update was found for extension_id that we'll
-  // attempt to download and install.
-  void NotifyUpdateFound(const std::string& extension_id);
-
-  // Send a notification if we're finished updating.
-  void NotifyIfFinished();
-
-  // Adds a set of ids to in_progress_ids_.
-  void AddToInProgress(const std::set<std::string>& ids);
-
-  // Removes a set of ids from in_progress_ids_.
-  void RemoveFromInProgress(const std::set<std::string>& ids);
-
-  // If a CRX file has been fetched but not installed, and no install is
-  // currently running, start installing.  Returns true if an install was
-  // started.
-  bool MaybeInstallCRXFile();
-
-  // content::NotificationObserver implementation.
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE;
-
-  // Whether Start() has been called but not Stop().
-  bool alive_;
-
-  base::WeakPtrFactory<ExtensionUpdater> weak_ptr_factory_;
-
-  // Outstanding url fetch requests for manifests and updates.
-  scoped_ptr<content::URLFetcher> manifest_fetcher_;
-  scoped_ptr<content::URLFetcher> extension_fetcher_;
-
-  // Pending manifests and extensions to be fetched when the appropriate fetcher
-  // is available.
-  std::deque<ManifestFetchData*> manifests_pending_;
-  std::deque<ExtensionFetch> extensions_pending_;
-
-  // The manifest currently being fetched (if any).
-  scoped_ptr<ManifestFetchData> current_manifest_fetch_;
-
-  // The extension currently being fetched (if any).
-  ExtensionFetch current_extension_fetch_;
-
-  // Pointer back to the service that owns this ExtensionUpdater.
-  ExtensionServiceInterface* service_;
-
-  base::OneShotTimer<ExtensionUpdater> timer_;
-  int frequency_seconds_;
-
-  bool will_check_soon_;
-
-  ExtensionPrefs* extension_prefs_;
-  PrefService* prefs_;
-  Profile* profile_;
-  bool blacklist_checks_enabled_;
-
-  // The ids of extensions that have in-progress update checks.
-  std::set<std::string> in_progress_ids_;
-
-  // Observes CRX installs we initiate.
-  content::NotificationRegistrar registrar_;
-
-  // True when a CrxInstaller is doing an install.  Used in MaybeUpdateCrxFile()
-  // to keep more than one install from running at once.
-  bool crx_install_is_running_;
-
-  // Fetched CRX files waiting to be installed.
-  std::stack<FetchedCRXFile> fetched_crx_files_;
-
-  FRIEND_TEST_ALL_PREFIXES(ExtensionUpdaterTest, TestStartUpdateCheckMemory);
-  FRIEND_TEST_ALL_PREFIXES(ExtensionUpdaterTest, TestAfterStopBehavior);
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
-};
-
-#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
diff --git a/chrome/browser/extensions/pending_extension_manager.h b/chrome/browser/extensions/pending_extension_manager.h
index 098d5f1..859bc860 100644
--- a/chrome/browser/extensions/pending_extension_manager.h
+++ b/chrome/browser/extensions/pending_extension_manager.h
@@ -14,6 +14,14 @@
 
 class ExtensionServiceInterface;
 class GURL;
+class PendingExtensionManager;
+
+namespace extensions {
+class ExtensionUpdaterTest;
+void SetupPendingExtensionManagerForTest(
+    int count, const GURL& update_url,
+    PendingExtensionManager* pending_extension_manager);
+}
 
 // Class PendingExtensionManager manages the set of extensions which are
 // being installed or updated. In general, installation and updates take
@@ -110,8 +118,8 @@
 
   FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
                            UpdatePendingExtensionAlreadyInstalled);
-  friend class ExtensionUpdaterTest;
-  friend void SetupPendingExtensionManagerForTest(
+  friend class extensions::ExtensionUpdaterTest;
+  friend void extensions::SetupPendingExtensionManagerForTest(
       int count, const GURL& update_url,
       PendingExtensionManager* pending_extension_manager);
 
diff --git a/chrome/browser/extensions/updater/OWNERS b/chrome/browser/extensions/updater/OWNERS
new file mode 100644
index 0000000..f5720f0
--- /dev/null
+++ b/chrome/browser/extensions/updater/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
diff --git a/chrome/browser/extensions/updater/extension_downloader.cc b/chrome/browser/extensions/updater/extension_downloader.cc
new file mode 100644
index 0000000..fbf960f
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader.cc
@@ -0,0 +1,649 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_handle.h"
+#include "base/metrics/histogram.h"
+#include "base/platform_file.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/version.h"
+#include "chrome/browser/extensions/updater/safe_manifest_parser.h"
+#include "chrome/browser/metrics/metrics_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/common/url_fetcher.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_status.h"
+
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+
+namespace extensions {
+
+const char ExtensionDownloader::kBlacklistAppID[] = "com.google.crx.blacklist";
+
+namespace {
+
+const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
+const char kDefaultInstallSource[] = "";
+
+// TODO(skerner): It would be nice to know if the file system failure
+// happens when creating a temp file or when writing to it. Knowing this
+// will require changes to URLFetcher.
+enum FileWriteResult {
+  SUCCESS = 0,
+  CANT_CREATE_OR_WRITE_TEMP_CRX,
+  CANT_READ_CRX_FILE,
+  NUM_FILE_WRITE_RESULTS,
+};
+
+void RecordFileUpdateHistogram(FileWriteResult file_write_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrxAsFile",
+                            file_write_result,
+                            NUM_FILE_WRITE_RESULTS);
+}
+
+void CheckThatCRXIsReadable(const FilePath& crx_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+  FileWriteResult file_write_result = SUCCESS;
+
+  // Open the file in the same way
+  // SandboxExtensionUnpacker::ValidateSigniture() will.
+  ScopedStdioHandle file(file_util::OpenFile(crx_path, "rb"));
+  if (!file.get()) {
+    LOG(ERROR) << "Can't read CRX file written for update at path "
+               << crx_path.value().c_str();
+    file_write_result = CANT_READ_CRX_FILE;
+  }
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RecordFileUpdateHistogram, file_write_result));
+}
+
+// Record the result of writing a CRX file. Will be used to understand
+// high failure rates of CRX installs in the field.  If |success| is
+// true, |crx_path| should be set to the path to the CRX file.
+void RecordCRXWriteHistogram(bool success, const FilePath& crx_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (!success) {
+    // We know there was an error writing the file.
+    RecordFileUpdateHistogram(CANT_CREATE_OR_WRITE_TEMP_CRX);
+  } else {
+    // Test that the file can be read. Based on histograms in
+    // SandboxExtensionUnpacker, we know that many CRX files
+    // can not be read. Try reading.
+    BrowserThread::PostTask(
+        BrowserThread::FILE, FROM_HERE,
+        base::Bind(&CheckThatCRXIsReadable, crx_path));
+  }
+}
+
+}  // namespace
+
+ExtensionDownloader::ExtensionFetch::ExtensionFetch()
+    : id(""),
+      url(),
+      package_hash(""),
+      version("") {}
+
+ExtensionDownloader::ExtensionFetch::ExtensionFetch(
+    const std::string& id,
+    const GURL& url,
+    const std::string& package_hash,
+    const std::string& version)
+    : id(id), url(url), package_hash(package_hash), version(version) {}
+
+ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {}
+
+ExtensionDownloader::ExtensionDownloader(
+    ExtensionDownloaderDelegate* delegate,
+    net::URLRequestContextGetter* request_context)
+    : delegate_(delegate),
+      request_context_(request_context),
+      weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+  DCHECK(delegate_);
+  DCHECK(request_context_);
+}
+
+ExtensionDownloader::~ExtensionDownloader() {
+  for (FetchMap::iterator it = fetches_preparing_.begin();
+       it != fetches_preparing_.end(); ++it) {
+    STLDeleteElements(&it->second);
+  }
+  STLDeleteElements(&manifests_pending_);
+}
+
+bool ExtensionDownloader::AddExtension(const Extension& extension) {
+  // Skip extensions with empty update URLs converted from user
+  // scripts.
+  if (extension.converted_from_user_script() &&
+      extension.update_url().is_empty()) {
+    return false;
+  }
+
+  // If the extension updates itself from the gallery, ignore any update URL
+  // data.  At the moment there is no extra data that an extension can
+  // communicate to the the gallery update servers.
+  std::string update_url_data;
+  if (!extension.UpdatesFromGallery())
+    update_url_data = delegate_->GetUpdateUrlData(extension.id());
+
+  return AddExtensionData(extension.id(), *extension.version(),
+                          extension.GetType(), extension.update_url(),
+                          update_url_data);
+}
+
+bool ExtensionDownloader::AddPendingExtension(const std::string& id,
+                                              const GURL& update_url) {
+  // Use a zero version to ensure that a pending extension will always
+  // be updated, and thus installed (assuming all extensions have
+  // non-zero versions).
+  Version version("0.0.0.0");
+  DCHECK(version.IsValid());
+
+  return AddExtensionData(id, version, Extension::TYPE_UNKNOWN, update_url, "");
+}
+
+void ExtensionDownloader::StartAllPending() {
+  ReportStats();
+  url_stats_ = URLStats();
+
+  for (FetchMap::iterator it = fetches_preparing_.begin();
+       it != fetches_preparing_.end(); ++it) {
+    const std::vector<ManifestFetchData*>& list = it->second;
+    for (size_t i = 0; i < list.size(); ++i) {
+      StartUpdateCheck(list[i]);
+    }
+  }
+  fetches_preparing_.clear();
+}
+
+void ExtensionDownloader::StartBlacklistUpdate(
+    const std::string& version,
+   const ManifestFetchData::PingData& ping_data) {
+  // Note: it is very important that we use the https version of the update
+  // url here to avoid DNS hijacking of the blacklist, which is not validated
+  // by a public key signature like .crx files are.
+  ManifestFetchData* blacklist_fetch =
+      new ManifestFetchData(extension_urls::GetWebstoreUpdateUrl(true));
+  blacklist_fetch->AddExtension(kBlacklistAppID, version, &ping_data, "",
+                                kDefaultInstallSource);
+  StartUpdateCheck(blacklist_fetch);
+}
+
+bool ExtensionDownloader::AddExtensionData(const std::string& id,
+                                           const Version& version,
+                                           Extension::Type extension_type,
+                                           GURL update_url,
+                                           const std::string& update_url_data) {
+  // Skip extensions with non-empty invalid update URLs.
+  if (!update_url.is_empty() && !update_url.is_valid()) {
+    LOG(WARNING) << "Extension " << id << " has invalid update url "
+                 << update_url;
+    return false;
+  }
+
+  // Skip extensions with empty IDs.
+  if (id.empty()) {
+    LOG(WARNING) << "Found extension with empty ID";
+    return false;
+  }
+
+  if (update_url.DomainIs("google.com")) {
+    url_stats_.google_url_count++;
+  } else if (update_url.is_empty()) {
+    url_stats_.no_url_count++;
+    // Fill in default update URL.
+    //
+    // TODO(akalin): Figure out if we should use the HTTPS version.
+    update_url = extension_urls::GetWebstoreUpdateUrl(false);
+  } else {
+    url_stats_.other_url_count++;
+  }
+
+  switch (extension_type) {
+    case Extension::TYPE_THEME:
+      ++url_stats_.theme_count;
+      break;
+    case Extension::TYPE_EXTENSION:
+    case Extension::TYPE_USER_SCRIPT:
+      ++url_stats_.extension_count;
+      break;
+    case Extension::TYPE_HOSTED_APP:
+    case Extension::TYPE_PACKAGED_APP:
+      ++url_stats_.app_count;
+      break;
+    case Extension::TYPE_UNKNOWN:
+    default:
+      ++url_stats_.pending_count;
+      break;
+  }
+
+  std::vector<GURL> update_urls;
+  update_urls.push_back(update_url);
+  // If UMA is enabled, also add to ManifestFetchData for the
+  // webstore update URL.
+  if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
+      MetricsServiceHelper::IsMetricsReportingEnabled()) {
+    update_urls.push_back(extension_urls::GetWebstoreUpdateUrl(false));
+  }
+
+  for (size_t i = 0; i < update_urls.size(); ++i) {
+    DCHECK(!update_urls[i].is_empty());
+    DCHECK(update_urls[i].is_valid());
+
+    std::string install_source = i == 0 ?
+        kDefaultInstallSource : kNotFromWebstoreInstallSource;
+
+    ManifestFetchData::PingData ping_data;
+    ManifestFetchData::PingData* optional_ping_data = NULL;
+    if (delegate_->GetPingDataForExtension(id, &ping_data))
+      optional_ping_data = &ping_data;
+
+    // Find or create a ManifestFetchData to add this extension to.
+    ManifestFetchData* fetch = NULL;
+    FetchMap::iterator existing_iter = fetches_preparing_.find(update_urls[i]);
+    if (existing_iter != fetches_preparing_.end() &&
+        !existing_iter->second.empty()) {
+      // Try to add to the ManifestFetchData at the end of the list.
+      ManifestFetchData* existing_fetch = existing_iter->second.back();
+      if (existing_fetch->AddExtension(id, version.GetString(),
+                                       optional_ping_data, update_url_data,
+                                       install_source)) {
+        fetch = existing_fetch;
+      }
+    }
+    if (!fetch) {
+      // Otherwise add a new element to the list, if the list doesn't exist or
+      // if its last element is already full.
+      fetch = new ManifestFetchData(update_urls[i]);
+      fetches_preparing_[update_urls[i]].push_back(fetch);
+      bool added = fetch->AddExtension(id, version.GetString(),
+                                       optional_ping_data,
+                                       update_url_data,
+                                       install_source);
+      DCHECK(added);
+    }
+  }
+
+  return true;
+}
+
+void ExtensionDownloader::ReportStats() const {
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
+                           url_stats_.extension_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
+                           url_stats_.theme_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
+                           url_stats_.app_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
+                           url_stats_.pending_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
+                           url_stats_.google_url_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
+                           url_stats_.other_url_count);
+  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
+                           url_stats_.no_url_count);
+}
+
+void ExtensionDownloader::StartUpdateCheck(ManifestFetchData* fetch_data) {
+  scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
+  const std::set<std::string>& id_set(fetch_data->extension_ids());
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kDisableBackgroundNetworking)) {
+    NotifyExtensionsDownloadFailed(id_set,
+                                   ExtensionDownloaderDelegate::DISABLED);
+    return;
+  }
+
+  std::deque<ManifestFetchData*>::const_iterator i;
+  for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
+    if (fetch_data->full_url() == (*i)->full_url()) {
+      // This url is already scheduled to be fetched.
+      return;
+    }
+  }
+
+  if (manifest_fetcher_.get() != NULL) {
+    if (manifest_fetcher_->GetURL() != fetch_data->full_url()) {
+      manifests_pending_.push_back(scoped_fetch_data.release());
+    }
+  } else {
+    UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
+        fetch_data->full_url().possibly_invalid_spec().length());
+
+    if (VLOG_IS_ON(2)) {
+      std::vector<std::string> id_vector(id_set.begin(), id_set.end());
+      std::string id_list = JoinString(id_vector, ',');
+      VLOG(2) << "Fetching " << fetch_data->full_url() << " for "
+              << id_list;
+    }
+
+    current_manifest_fetch_.swap(scoped_fetch_data);
+    manifest_fetcher_.reset(content::URLFetcher::Create(
+        kManifestFetcherId, fetch_data->full_url(), content::URLFetcher::GET,
+        this));
+    manifest_fetcher_->SetRequestContext(request_context_);
+    manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+                                    net::LOAD_DO_NOT_SAVE_COOKIES |
+                                    net::LOAD_DISABLE_CACHE);
+    manifest_fetcher_->Start();
+  }
+}
+
+void ExtensionDownloader::OnURLFetchComplete(
+    const content::URLFetcher* source) {
+  VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
+
+  if (source == manifest_fetcher_.get()) {
+    std::string data;
+    source->GetResponseAsString(&data);
+    OnManifestFetchComplete(source->GetURL(),
+                            source->GetStatus(),
+                            source->GetResponseCode(),
+                            data);
+  } else if (source == extension_fetcher_.get()) {
+    OnCRXFetchComplete(source,
+                       source->GetURL(),
+                       source->GetStatus(),
+                       source->GetResponseCode());
+  } else {
+    NOTREACHED();
+  }
+}
+
+void ExtensionDownloader::OnManifestFetchComplete(
+    const GURL& url,
+    const net::URLRequestStatus& status,
+    int response_code,
+    const std::string& data) {
+  // We want to try parsing the manifest, and if it indicates updates are
+  // available, we want to fire off requests to fetch those updates.
+  if (status.status() == net::URLRequestStatus::SUCCESS &&
+      (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
+    VLOG(2) << "beginning manifest parse for " << url;
+    scoped_refptr<SafeManifestParser> safe_parser(
+        new SafeManifestParser(
+            data,
+            current_manifest_fetch_.release(),
+            base::Bind(&ExtensionDownloader::HandleManifestResults,
+                       weak_ptr_factory_.GetWeakPtr())));
+    safe_parser->Start();
+  } else {
+    // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
+    VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
+            << "' response code:" << response_code;
+    NotifyExtensionsDownloadFailed(
+        current_manifest_fetch_->extension_ids(),
+        ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED);
+  }
+  manifest_fetcher_.reset();
+  current_manifest_fetch_.reset();
+
+  // If we have any pending manifest requests, fire off the next one.
+  if (!manifests_pending_.empty()) {
+    ManifestFetchData* manifest_fetch = manifests_pending_.front();
+    manifests_pending_.pop_front();
+    StartUpdateCheck(manifest_fetch);
+  }
+}
+
+void ExtensionDownloader::HandleManifestResults(
+    const ManifestFetchData& fetch_data,
+    const UpdateManifest::Results* results) {
+  // Keep a list of extensions that will not be updated, so that the |delegate_|
+  // can be notified once we're done here.
+  std::set<std::string> not_updated(fetch_data.extension_ids());
+
+  if (!results) {
+    NotifyExtensionsDownloadFailed(
+        not_updated,
+        ExtensionDownloaderDelegate::MANIFEST_INVALID);
+    return;
+  }
+
+  // Examine the parsed manifest and kick off fetches of any new crx files.
+  std::vector<int> updates;
+  DetermineUpdates(fetch_data, *results, &updates);
+  for (size_t i = 0; i < updates.size(); i++) {
+    const UpdateManifest::Result* update = &(results->list.at(updates[i]));
+    const std::string& id = update->extension_id;
+    not_updated.erase(id);
+    if (id != kBlacklistAppID) {
+      NotifyUpdateFound(update->extension_id);
+    } else {
+      // The URL of the blacklist file is returned by the server and we need to
+      // be sure that we continue to be able to reliably detect whether a URL
+      // references a blacklist file.
+      DCHECK(extension_urls::IsBlacklistUpdateUrl(update->crx_url))
+          << update->crx_url;
+    }
+    FetchUpdatedExtension(update->extension_id, update->crx_url,
+                          update->package_hash, update->version);
+  }
+
+  // If the manifest response included a <daystart> element, we want to save
+  // that value for any extensions which had sent a ping in the request.
+  if (fetch_data.base_url().DomainIs("google.com") &&
+      results->daystart_elapsed_seconds >= 0) {
+    Time day_start =
+        Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
+
+    const std::set<std::string>& extension_ids = fetch_data.extension_ids();
+    std::set<std::string>::const_iterator i;
+    for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
+      const std::string& id = *i;
+      ExtensionDownloaderDelegate::PingResult& result = ping_results_[id];
+      result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL);
+      result.day_start = day_start;
+    }
+  }
+
+  NotifyExtensionsDownloadFailed(
+      not_updated,
+      ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE);
+}
+
+void ExtensionDownloader::DetermineUpdates(
+    const ManifestFetchData& fetch_data,
+    const UpdateManifest::Results& possible_updates,
+    std::vector<int>* result) {
+  // This will only be valid if one of possible_updates specifies
+  // browser_min_version.
+  Version browser_version;
+
+  for (size_t i = 0; i < possible_updates.list.size(); i++) {
+    const UpdateManifest::Result* update = &possible_updates.list[i];
+    const std::string& id = update->extension_id;
+
+    if (!fetch_data.Includes(id)) {
+      VLOG(2) << "Ignoring " << id << " from this manifest";
+      continue;
+    }
+
+    if (VLOG_IS_ON(2)) {
+      if (update->version.empty())
+        VLOG(2) << "manifest indicates " << id << " has no update";
+      else
+        VLOG(2) << "manifest indicates " << id
+                << " latest version is '" << update->version << "'";
+    }
+
+    if (!delegate_->IsExtensionPending(id)) {
+      // If we're not installing pending extension, and the update
+      // version is the same or older than what's already installed,
+      // we don't want it.
+      std::string version;
+      if (!delegate_->GetExtensionExistingVersion(id, &version)) {
+        VLOG(2) << id << " is not installed";
+        continue;
+      }
+
+      VLOG(2) << id << " is at '" << version << "'";
+
+      Version existing_version(version);
+      Version update_version(update->version);
+
+      if (!update_version.IsValid() ||
+          update_version.CompareTo(existing_version) <= 0) {
+        continue;
+      }
+    }
+
+    // If the update specifies a browser minimum version, do we qualify?
+    if (update->browser_min_version.length() > 0) {
+      // First determine the browser version if we haven't already.
+      if (!browser_version.IsValid()) {
+        chrome::VersionInfo version_info;
+        if (version_info.is_valid())
+          browser_version = Version(version_info.Version());
+      }
+      Version browser_min_version(update->browser_min_version);
+      if (browser_version.IsValid() && browser_min_version.IsValid() &&
+          browser_min_version.CompareTo(browser_version) > 0) {
+        // TODO(asargent) - We may want this to show up in the extensions UI
+        // eventually. (http://crbug.com/12547).
+        LOG(WARNING) << "Updated version of extension " << id
+                     << " available, but requires chrome version "
+                     << update->browser_min_version;
+        continue;
+      }
+    }
+    VLOG(2) << "will try to update " << id;
+    result->push_back(i);
+  }
+}
+
+  // Begins (or queues up) download of an updated extension.
+void ExtensionDownloader::FetchUpdatedExtension(const std::string& id,
+                                                const GURL& url,
+                                                const std::string& hash,
+                                                const std::string& version) {
+  for (std::deque<ExtensionFetch>::const_iterator iter =
+           extensions_pending_.begin();
+       iter != extensions_pending_.end(); ++iter) {
+    if (iter->id == id || iter->url == url) {
+      return;  // already scheduled
+    }
+  }
+
+  if (extension_fetcher_.get() != NULL) {
+    if (extension_fetcher_->GetURL() != url) {
+      extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
+    }
+  } else {
+    extension_fetcher_.reset(content::URLFetcher::Create(
+        kExtensionFetcherId, url, content::URLFetcher::GET, this));
+    extension_fetcher_->SetRequestContext(request_context_);
+    extension_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+                                     net::LOAD_DO_NOT_SAVE_COOKIES |
+                                     net::LOAD_DISABLE_CACHE);
+    // Download CRX files to a temp file. The blacklist is small and will be
+    // processed in memory, so it is fetched into a string.
+    if (id != kBlacklistAppID) {
+      extension_fetcher_->SaveResponseToTemporaryFile(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
+    }
+
+    VLOG(2) << "Starting fetch of " << url << " for " << id;
+
+    extension_fetcher_->Start();
+    current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
+  }
+}
+
+void ExtensionDownloader::OnCRXFetchComplete(
+    const content::URLFetcher* source,
+    const GURL& url,
+    const net::URLRequestStatus& status,
+    int response_code) {
+  const std::string& id = current_extension_fetch_.id;
+  const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id];
+
+  base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+  if (source->FileErrorOccurred(&error_code)) {
+    LOG(ERROR) << "Failed to write update CRX with id " << id << ". "
+               << "Error code is "<< error_code;
+    RecordCRXWriteHistogram(false, FilePath());
+    delegate_->OnExtensionDownloadFailed(
+        id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping);
+  } else if (status.status() == net::URLRequestStatus::SUCCESS &&
+      (response_code == 200 || url.SchemeIsFile())) {
+    if (id == kBlacklistAppID) {
+      std::string data;
+      source->GetResponseAsString(&data);
+      // TODO(asargent): try to get rid of this special case for the blacklist
+      // to simplify the delegate's interface.
+      delegate_->OnBlacklistDownloadFinished(
+          data, current_extension_fetch_.package_hash,
+          current_extension_fetch_.version, ping);
+    } else {
+      FilePath crx_path;
+      // Take ownership of the file at |crx_path|.
+      CHECK(source->GetResponseAsFilePath(true, &crx_path));
+      RecordCRXWriteHistogram(true, crx_path);
+      delegate_->OnExtensionDownloadFinished(id, crx_path, url, ping);
+    }
+  } else {
+    // TODO(asargent) do things like exponential backoff, handling
+    // 503 Service Unavailable / Retry-After headers, etc. here.
+    // (http://crbug.com/12546).
+    VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
+            << "' response code:" << response_code;
+    delegate_->OnExtensionDownloadFailed(
+        id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping);
+  }
+
+  extension_fetcher_.reset();
+  current_extension_fetch_ = ExtensionFetch();
+  ping_results_.erase(id);
+
+  // If there are any pending downloads left, start the next one.
+  if (!extensions_pending_.empty()) {
+    ExtensionFetch next = extensions_pending_.front();
+    extensions_pending_.pop_front();
+    FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
+  }
+}
+
+void ExtensionDownloader::NotifyExtensionsDownloadFailed(
+    const std::set<std::string>& extension_ids,
+    ExtensionDownloaderDelegate::Error error) {
+  for (std::set<std::string>::const_iterator it = extension_ids.begin();
+       it != extension_ids.end(); ++it) {
+    delegate_->OnExtensionDownloadFailed(*it, error, ping_results_[*it]);
+    ping_results_.erase(*it);
+  }
+}
+
+void ExtensionDownloader::NotifyUpdateFound(const std::string& id) {
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+      content::NotificationService::AllBrowserContextsAndSources(),
+      content::Details<const std::string>(&id));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_downloader.h b/chrome/browser/extensions/updater/extension_downloader.h
new file mode 100644
index 0000000..b59c1259
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
+#pragma once
+
+#include <deque>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/update_manifest.h"
+#include "content/public/common/url_fetcher_delegate.h"
+#include "googleurl/src/gurl.h"
+
+class Version;
+
+namespace net {
+class URLRequestContextGetter;
+class URLRequestStatus;
+}
+
+namespace extensions {
+
+class ExtensionUpdaterTest;
+
+// A class that checks for updates of a given list of extensions, and downloads
+// the crx file when updates are found. It uses a |ExtensionDownloaderDelegate|
+// that takes ownership of the downloaded crx files, and handles events during
+// the update check.
+class ExtensionDownloader : public content::URLFetcherDelegate {
+ public:
+  // |delegate| is stored as a raw pointer and must outlive the
+  // ExtensionDownloader.
+  ExtensionDownloader(ExtensionDownloaderDelegate* delegate,
+                      net::URLRequestContextGetter* request_context);
+  virtual ~ExtensionDownloader();
+
+  // Adds |extension| to the list of extensions to check for updates.
+  // Returns false if the |extension| can't be updated due to invalid details.
+  // In that case, no callbacks will be performed on the |delegate_|.
+  bool AddExtension(const Extension& extension);
+
+  // Adds extension |id| to the list of extensions to check for updates.
+  // Returns false if the |id| can't be updated due to invalid details.
+  // In that case, no callbacks will be performed on the |delegate_|.
+  bool AddPendingExtension(const std::string& id, const GURL& update_url);
+
+  // Schedules a fetch of the manifest of all the extensions added with
+  // AddExtension() and AddPendingExtension().
+  void StartAllPending();
+
+  // Schedules an update check of the blacklist.
+  void StartBlacklistUpdate(const std::string& version,
+                            const ManifestFetchData::PingData& ping_data);
+
+  // These are needed for unit testing, to help identify the correct mock
+  // URLFetcher objects.
+  static const int kManifestFetcherId = 1;
+  static const int kExtensionFetcherId = 2;
+
+  // Update AppID for extension blacklist.
+  static const char kBlacklistAppID[];
+
+ private:
+  friend class ExtensionUpdaterTest;
+
+  // These counters are bumped as extensions are added to be fetched. They
+  // are then recorded as UMA metrics when all the extensions have been added.
+  struct URLStats {
+    URLStats()
+        : no_url_count(0),
+          google_url_count(0),
+          other_url_count(0),
+          extension_count(0),
+          theme_count(0),
+          app_count(0),
+          pending_count(0) {}
+
+    int no_url_count, google_url_count, other_url_count;
+    int extension_count, theme_count, app_count, pending_count;
+  };
+
+  // We need to keep track of some information associated with a url
+  // when doing a fetch.
+  struct ExtensionFetch {
+    ExtensionFetch();
+    ExtensionFetch(const std::string& id, const GURL& url,
+                   const std::string& package_hash, const std::string& version);
+    ~ExtensionFetch();
+
+    std::string id;
+    GURL url;
+    std::string package_hash;
+    std::string version;
+  };
+
+  // Helper for AddExtension() and AddPendingExtension().
+  bool AddExtensionData(const std::string& id,
+                        const Version& version,
+                        Extension::Type extension_type,
+                        GURL update_url,
+                        const std::string& update_url_data);
+
+  // Adds all recorded stats taken so far to histogram counts.
+  void ReportStats() const;
+
+  // Begins an update check. Takes ownership of |fetch_data|.
+  void StartUpdateCheck(ManifestFetchData* fetch_data);
+
+  // content::URLFetcherDelegate implementation.
+  virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
+
+  // Handles the result of a manifest fetch.
+  void OnManifestFetchComplete(const GURL& url,
+                               const net::URLRequestStatus& status,
+                               int response_code,
+                               const std::string& data);
+
+  // Once a manifest is parsed, this starts fetches of any relevant crx files.
+  // If |results| is null, it means something went wrong when parsing it.
+  void HandleManifestResults(const ManifestFetchData& fetch_data,
+                             const UpdateManifest::Results* results);
+
+  // Given a list of potential updates, returns the indices of the ones that are
+  // applicable (are actually a new version, etc.) in |result|.
+  void DetermineUpdates(const ManifestFetchData& fetch_data,
+                        const UpdateManifest::Results& possible_updates,
+                        std::vector<int>* result);
+
+  // Begins (or queues up) download of an updated extension.
+  void FetchUpdatedExtension(const std::string& id,
+                             const GURL& url,
+                             const std::string& hash,
+                             const std::string& version);
+
+  // Handles the result of a crx fetch.
+  void OnCRXFetchComplete(const content::URLFetcher* source,
+                          const GURL& url,
+                          const net::URLRequestStatus& status,
+                          int response_code);
+
+  // Invokes OnExtensionDownloadFailed() on the |delegate_| for each extension
+  // in the set, with |error| as the reason for failure.
+  void NotifyExtensionsDownloadFailed(const std::set<std::string>& id_set,
+                                      ExtensionDownloaderDelegate::Error error);
+
+  // Send a notification that an update was found for |id| that we'll
+  // attempt to download.
+  void NotifyUpdateFound(const std::string& id);
+
+  // The delegate that receives the crx files downloaded by the
+  // ExtensionDownloader, and that fills in optional ping and update url data.
+  ExtensionDownloaderDelegate* delegate_;
+
+  // The request context to use for the URLFetchers.
+  net::URLRequestContextGetter* request_context_;
+
+  // Used to create WeakPtrs to |this|.
+  base::WeakPtrFactory<ExtensionDownloader> weak_ptr_factory_;
+
+  // Collects UMA samples that are reported when ReportStats() is called.
+  URLStats url_stats_;
+
+  // List of data on fetches we're going to do. We limit the number of
+  // extensions grouped together in one batch to avoid running into the limits
+  // on the length of http GET requests, so there might be multiple
+  // ManifestFetchData* objects with the same base_url.
+  typedef std::map<GURL, std::vector<ManifestFetchData*> > FetchMap;
+  FetchMap fetches_preparing_;
+
+  // Outstanding url fetch requests for manifests and updates.
+  scoped_ptr<content::URLFetcher> manifest_fetcher_;
+  scoped_ptr<content::URLFetcher> extension_fetcher_;
+
+  // Pending manifests and extensions to be fetched when the appropriate fetcher
+  // is available.
+  std::deque<ManifestFetchData*> manifests_pending_;
+  std::deque<ExtensionFetch> extensions_pending_;
+
+  // The manifest currently being fetched (if any).
+  scoped_ptr<ManifestFetchData> current_manifest_fetch_;
+
+  // The extension currently being fetched (if any).
+  ExtensionFetch current_extension_fetch_;
+
+  // Maps an extension-id to its PingResult data.
+  std::map<std::string, ExtensionDownloaderDelegate::PingResult> ping_results_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionDownloader);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_H_
diff --git a/chrome/browser/extensions/updater/extension_downloader_delegate.cc b/chrome/browser/extensions/updater/extension_downloader_delegate.cc
new file mode 100644
index 0000000..3d3eba9
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader_delegate.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+
+namespace extensions {
+
+ExtensionDownloaderDelegate::PingResult::PingResult() : did_ping(false) {}
+
+ExtensionDownloaderDelegate::PingResult::~PingResult() {}
+
+ExtensionDownloaderDelegate::~ExtensionDownloaderDelegate() {}
+
+void ExtensionDownloaderDelegate::OnExtensionDownloadFailed(
+    const std::string& id,
+    ExtensionDownloaderDelegate::Error error,
+    const ExtensionDownloaderDelegate::PingResult& ping_result) {}
+
+bool ExtensionDownloaderDelegate::GetPingDataForExtension(
+    const std::string& id,
+    ManifestFetchData::PingData* ping) {
+  return false;
+}
+
+std::string ExtensionDownloaderDelegate::GetUpdateUrlData(
+    const std::string& id) {
+  return std::string();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_downloader_delegate.h b/chrome/browser/extensions/updater/extension_downloader_delegate.h
new file mode 100644
index 0000000..4171eebd
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_downloader_delegate.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
+#pragma once
+
+#include <string>
+
+#include "base/time.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+
+class FilePath;
+class GURL;
+
+namespace extensions {
+
+class ExtensionDownloaderDelegate {
+ public:
+  virtual ~ExtensionDownloaderDelegate();
+
+  // Passed as an argument to ExtensionDownloader::OnExtensionDownloadFailed()
+  // to detail the reason for the failure.
+  enum Error {
+    // Background networking is disabled.
+    DISABLED,
+
+    // Failed to fetch the manifest for this extension.
+    MANIFEST_FETCH_FAILED,
+
+    // The manifest couldn't be parsed.
+    MANIFEST_INVALID,
+
+    // The manifest was fetched and parsed, and there are no updates for
+    // this extension.
+    NO_UPDATE_AVAILABLE,
+
+    // There was an update for this extension but the download of the crx
+    // failed.
+    CRX_FETCH_FAILED,
+  };
+
+  // Passed as an argument to the completion callbacks to signal whether
+  // the extension update sent a ping.
+  struct PingResult {
+    PingResult();
+    ~PingResult();
+
+    // Whether a ping was sent.
+    bool did_ping;
+
+    // The start of day, from the server's perspective. This is only valid
+    // when |did_ping| is true.
+    base::Time day_start;
+  };
+
+  // One of the following 3 methods is always invoked for a given extension
+  // id, if AddExtension() or AddPendingExtension() returned true when that
+  // extension was added to the ExtensionDownloader.
+
+  // Invoked if the extension couldn't be downloaded. |error| contains the
+  // failure reason.
+  virtual void OnExtensionDownloadFailed(const std::string& id,
+                                         Error error,
+                                         const PingResult& ping_result);
+
+  // Invoked if the extension had an update available and its crx was
+  // successfully downloaded to |path|. Ownership if that file is transferred
+  // to the delegate.
+  virtual void OnExtensionDownloadFinished(const std::string& id,
+                                           const FilePath& path,
+                                           const GURL& download_url,
+                                           const PingResult& ping_result) = 0;
+
+  // Same as OnExtensionDownloadFinished() but only for the kBlacklistAppID
+  // extension, which passes different data to the delegate.
+  virtual void OnBlacklistDownloadFinished(const std::string& data,
+                                           const std::string& package_hash,
+                                           const std::string& version,
+                                           const PingResult& ping_result) = 0;
+
+  // The remaining methods are used by the ExtensionDownloader to retrieve
+  // information about extensions from the delegate.
+
+  // Invoked to fill the PingData for the given extension id. Returns false
+  // if PingData should not be included for this extension's update check
+  // (this is the default).
+  virtual bool GetPingDataForExtension(const std::string& id,
+                                       ManifestFetchData::PingData* ping);
+
+  // Invoked to get the update url data for this extension's update url, if
+  // there is any. The default implementation returns an empty string.
+  virtual std::string GetUpdateUrlData(const std::string& id);
+
+  // Invoked to determine whether extension |id| is currently
+  // pending installation.
+  virtual bool IsExtensionPending(const std::string& id) = 0;
+
+  // Invoked to get the current version of extension |id|. Returns false if
+  // that extension is not installed.
+  virtual bool GetExtensionExistingVersion(const std::string& id,
+                                           std::string* version) = 0;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_DOWNLOADER_DELEGATE_H_
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
new file mode 100644
index 0000000..ff3fdaf
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/extension_updater.h"
+
+#include <algorithm>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/pending_extension_manager.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_set.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "crypto/sha2.h"
+
+using base::RandDouble;
+using base::RandInt;
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+using prefs::kExtensionBlacklistUpdateVersion;
+using prefs::kLastExtensionsUpdateCheck;
+using prefs::kNextExtensionsUpdateCheck;
+
+typedef extensions::ExtensionDownloaderDelegate::Error Error;
+typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
+
+namespace {
+
+// Wait at least 5 minutes after browser startup before we do any checks. If you
+// change this value, make sure to update comments where it is used.
+const int kStartupWaitSeconds = 60 * 5;
+
+// For sanity checking on update frequency - enforced in release mode only.
+const int kMinUpdateFrequencySeconds = 30;
+const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
+
+// When we've computed a days value, we want to make sure we don't send a
+// negative value (due to the system clock being set backwards, etc.), since -1
+// is a special sentinel value that means "never pinged", and other negative
+// values don't make sense.
+int SanitizeDays(int days) {
+  if (days < 0)
+    return 0;
+  return days;
+}
+
+// Calculates the value to use for the ping days parameter.
+int CalculatePingDays(const Time& last_ping_day) {
+  int days = extensions::ManifestFetchData::kNeverPinged;
+  if (!last_ping_day.is_null()) {
+    days = SanitizeDays((Time::Now() - last_ping_day).InDays());
+  }
+  return days;
+}
+
+int CalculateActivePingDays(const Time& last_active_ping_day,
+                            bool hasActiveBit) {
+  if (!hasActiveBit)
+    return 0;
+  if (last_active_ping_day.is_null())
+    return extensions::ManifestFetchData::kNeverPinged;
+  return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
+}
+
+}  // namespace
+
+namespace extensions {
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(const std::string& i,
+                                                 const FilePath& p,
+                                                 const GURL& u)
+    : id(i),
+      path(p),
+      download_url(u) {}
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
+    : id(""),
+      path(),
+      download_url() {}
+
+ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
+
+ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
+                                   ExtensionPrefs* extension_prefs,
+                                   PrefService* prefs,
+                                   Profile* profile,
+                                   int frequency_seconds)
+    : alive_(false),
+      weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+      service_(service), frequency_seconds_(frequency_seconds),
+      will_check_soon_(false), extension_prefs_(extension_prefs),
+      prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true),
+      crx_install_is_running_(false) {
+  DCHECK_GE(frequency_seconds_, 5);
+  DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
+#ifdef NDEBUG
+  // In Release mode we enforce that update checks don't happen too often.
+  frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
+#endif
+  frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
+}
+
+ExtensionUpdater::~ExtensionUpdater() {
+  Stop();
+}
+
+// The overall goal here is to balance keeping clients up to date while
+// avoiding a thundering herd against update servers.
+TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
+  DCHECK(alive_);
+  // If someone's testing with a quick frequency, just allow it.
+  if (frequency_seconds_ < kStartupWaitSeconds)
+    return TimeDelta::FromSeconds(frequency_seconds_);
+
+  // If we've never scheduled a check before, start at frequency_seconds_.
+  if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
+    return TimeDelta::FromSeconds(frequency_seconds_);
+
+  // If it's been a long time since our last actual check, we want to do one
+  // relatively soon.
+  Time now = Time::Now();
+  Time last = Time::FromInternalValue(prefs_->GetInt64(
+      kLastExtensionsUpdateCheck));
+  int days = (now - last).InDays();
+  if (days >= 30) {
+    // Wait 5-10 minutes.
+    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
+                                          kStartupWaitSeconds * 2));
+  } else if (days >= 14) {
+    // Wait 10-20 minutes.
+    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
+                                          kStartupWaitSeconds * 4));
+  } else if (days >= 3) {
+    // Wait 20-40 minutes.
+    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
+                                          kStartupWaitSeconds * 8));
+  }
+
+  // Read the persisted next check time, and use that if it isn't too soon.
+  // Otherwise pick something random.
+  Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
+      kNextExtensionsUpdateCheck));
+  Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
+  if (saved_next >= earliest) {
+    return saved_next - now;
+  } else {
+    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
+                                          frequency_seconds_));
+  }
+}
+
+void ExtensionUpdater::Start() {
+  DCHECK(!alive_);
+  // If these are NULL, then that means we've been called after Stop()
+  // has been called.
+  DCHECK(service_);
+  DCHECK(extension_prefs_);
+  DCHECK(prefs_);
+  DCHECK(profile_);
+  DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+  alive_ = true;
+  // Make sure our prefs are registered, then schedule the first check.
+  ScheduleNextCheck(DetermineFirstCheckDelay());
+}
+
+void ExtensionUpdater::Stop() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  alive_ = false;
+  service_ = NULL;
+  extension_prefs_ = NULL;
+  prefs_ = NULL;
+  profile_ = NULL;
+  timer_.Stop();
+  will_check_soon_ = false;
+  downloader_.reset();
+}
+
+void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
+  DCHECK(alive_);
+  DCHECK(!timer_.IsRunning());
+  DCHECK(target_delay >= TimeDelta::FromSeconds(1));
+
+  // Add +/- 10% random jitter.
+  double delay_ms = target_delay.InMillisecondsF();
+  double jitter_factor = (RandDouble() * .2) - 0.1;
+  delay_ms += delay_ms * jitter_factor;
+  TimeDelta actual_delay = TimeDelta::FromMilliseconds(
+      static_cast<int64>(delay_ms));
+
+  // Save the time of next check.
+  Time next = Time::Now() + actual_delay;
+  prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
+
+  timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
+}
+
+void ExtensionUpdater::TimerFired() {
+  DCHECK(alive_);
+  CheckNow();
+
+  // If the user has overridden the update frequency, don't bother reporting
+  // this.
+  if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
+    Time last = Time::FromInternalValue(prefs_->GetInt64(
+        kLastExtensionsUpdateCheck));
+    if (last.ToInternalValue() != 0) {
+      // Use counts rather than time so we can use minutes rather than millis.
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
+          (Time::Now() - last).InMinutes(),
+          TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
+          TimeDelta::FromDays(40).InMinutes(),
+          50);  // 50 buckets seems to be the default.
+    }
+  }
+
+  // Save the last check time, and schedule the next check.
+  int64 now = Time::Now().ToInternalValue();
+  prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
+  ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
+}
+
+void ExtensionUpdater::CheckSoon() {
+  DCHECK(alive_);
+  if (will_check_soon_)
+    return;
+  if (BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          base::Bind(&ExtensionUpdater::DoCheckSoon,
+                     weak_ptr_factory_.GetWeakPtr()))) {
+    will_check_soon_ = true;
+  } else {
+    NOTREACHED();
+  }
+}
+
+bool ExtensionUpdater::WillCheckSoon() const {
+  return will_check_soon_;
+}
+
+void ExtensionUpdater::DoCheckSoon() {
+  DCHECK(will_check_soon_);
+  CheckNow();
+  will_check_soon_ = false;
+}
+
+void ExtensionUpdater::CheckNow() {
+  VLOG(2) << "Starting update check";
+  DCHECK(alive_);
+  NotifyStarted();
+
+  if (!downloader_.get()) {
+    downloader_.reset(
+        new ExtensionDownloader(this, profile_->GetRequestContext()));
+  }
+
+  // Add fetch records for extensions that should be fetched by an update URL.
+  // These extensions are not yet installed. They come from group policy
+  // and external install sources.
+  const PendingExtensionManager* pending_extension_manager =
+      service_->pending_extension_manager();
+
+  std::set<std::string> pending_ids;
+  pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
+
+  std::set<std::string>::const_iterator iter;
+  for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
+    PendingExtensionInfo info;
+    bool found_id = pending_extension_manager->GetById(*iter, &info);
+    DCHECK(found_id);
+    if (!found_id)
+      continue;
+    if (!Extension::IsAutoUpdateableLocation(info.install_source())) {
+      VLOG(2) << "Extension " << *iter << " is not auto updateable";
+      continue;
+    }
+    if (downloader_->AddPendingExtension(*iter, info.update_url()))
+      in_progress_ids_.insert(*iter);
+  }
+
+  // Add fetch records for extensions that are installed and have an
+  // update URL.
+  const ExtensionSet* extensions = service_->extensions();
+  for (ExtensionSet::const_iterator iter = extensions->begin();
+       iter != extensions->end(); ++iter) {
+    const Extension& extension = **iter;
+    if (!Extension::IsAutoUpdateableLocation(extension.location())) {
+      VLOG(2) << "Extension " << extension.id() << " is not auto updateable";
+      continue;
+    }
+    // An extension might be overwritten by policy, and have its update url
+    // changed. Make sure existing extensions aren't fetched again, if a
+    // pending fetch for an extension with the same id already exists.
+    if (!ContainsKey(pending_ids, extension.id())) {
+      if (downloader_->AddExtension(extension))
+        in_progress_ids_.insert(extension.id());
+    }
+  }
+
+  // Start a fetch of the blacklist if needed.
+  if (blacklist_checks_enabled_) {
+    ManifestFetchData::PingData ping_data;
+    ping_data.rollcall_days =
+        CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
+    downloader_->StartBlacklistUpdate(
+        prefs_->GetString(kExtensionBlacklistUpdateVersion), ping_data);
+  }
+
+  // StartAllPending() will call OnExtensionUpdateCheckStarted() for each
+  // extension that is going to be checked.
+  downloader_->StartAllPending();
+
+  NotifyIfFinished();
+}
+
+void ExtensionUpdater::OnExtensionDownloadFailed(const std::string& id,
+                                                 Error error,
+                                                 const PingResult& ping) {
+  DCHECK(alive_);
+  UpdatePingData(id, ping);
+  in_progress_ids_.erase(id);
+  NotifyIfFinished();
+}
+
+void ExtensionUpdater::OnExtensionDownloadFinished(const std::string& id,
+                                                   const FilePath& path,
+                                                   const GURL& download_url,
+                                                   const PingResult& ping) {
+  DCHECK(alive_);
+  UpdatePingData(id, ping);
+
+  VLOG(2) << download_url << " written to " << path.value();
+
+  FetchedCRXFile fetched(id, path, download_url);
+  fetched_crx_files_.push(fetched);
+
+  // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
+  // starting the crx installer.
+  MaybeInstallCRXFile();
+}
+
+void ExtensionUpdater::OnBlacklistDownloadFinished(
+    const std::string& data,
+    const std::string& package_hash,
+    const std::string& version,
+    const PingResult& ping) {
+  DCHECK(alive_);
+  UpdatePingData(ExtensionDownloader::kBlacklistAppID, ping);
+  in_progress_ids_.erase(ExtensionDownloader::kBlacklistAppID);
+  NotifyIfFinished();
+
+  // Verify sha256 hash value.
+  char sha256_hash_value[crypto::kSHA256Length];
+  crypto::SHA256HashString(data, sha256_hash_value, crypto::kSHA256Length);
+  std::string hash_in_hex = base::HexEncode(sha256_hash_value,
+                                            crypto::kSHA256Length);
+
+  if (package_hash != hash_in_hex) {
+    NOTREACHED() << "Fetched blacklist checksum is not as expected. "
+        << "Expected: " << package_hash << " Actual: " << hash_in_hex;
+    return;
+  }
+  std::vector<std::string> blacklist;
+  base::SplitString(data, '\n', &blacklist);
+
+  // Tell ExtensionService to update prefs.
+  service_->UpdateExtensionBlacklist(blacklist);
+
+  // Update the pref value for blacklist version
+  prefs_->SetString(kExtensionBlacklistUpdateVersion, version);
+}
+
+bool ExtensionUpdater::GetPingDataForExtension(
+    const std::string& id,
+    ManifestFetchData::PingData* ping_data) {
+  DCHECK(alive_);
+  ping_data->rollcall_days = CalculatePingDays(
+      extension_prefs_->LastPingDay(id));
+  ping_data->active_days =
+      CalculateActivePingDays(extension_prefs_->LastActivePingDay(id),
+                              extension_prefs_->GetActiveBit(id));
+  return true;
+}
+
+std::string ExtensionUpdater::GetUpdateUrlData(const std::string& id) {
+  DCHECK(alive_);
+  return extension_prefs_->GetUpdateUrlData(id);
+}
+
+bool ExtensionUpdater::IsExtensionPending(const std::string& id) {
+  DCHECK(alive_);
+  return service_->pending_extension_manager()->IsIdPending(id);
+}
+
+bool ExtensionUpdater::GetExtensionExistingVersion(const std::string& id,
+                                                   std::string* version) {
+  DCHECK(alive_);
+  if (id == ExtensionDownloader::kBlacklistAppID) {
+    *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
+    return true;
+  }
+  const Extension* extension = service_->GetExtensionById(id, false);
+  if (!extension)
+    return false;
+  *version = extension->version()->GetString();
+  return true;
+}
+
+void ExtensionUpdater::UpdatePingData(const std::string& id,
+                                      const PingResult& ping_result) {
+  DCHECK(alive_);
+  if (ping_result.did_ping) {
+    if (id == ExtensionDownloader::kBlacklistAppID) {
+      extension_prefs_->SetBlacklistLastPingDay(ping_result.day_start);
+    } else if (service_->GetExtensionById(id, true) != NULL) {
+      extension_prefs_->SetLastPingDay(id, ping_result.day_start);
+    }
+  }
+  if (extension_prefs_->GetActiveBit(id)) {
+    extension_prefs_->SetActiveBit(id, false);
+    extension_prefs_->SetLastActivePingDay(id, ping_result.day_start);
+  }
+}
+
+void ExtensionUpdater::MaybeInstallCRXFile() {
+  if (crx_install_is_running_ || fetched_crx_files_.empty())
+    return;
+
+  while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
+    const FetchedCRXFile& crx_file = fetched_crx_files_.top();
+
+    VLOG(2) << "updating " << crx_file.id << " with " << crx_file.path.value();
+
+    // The ExtensionService is now responsible for cleaning up the temp file
+    // at |crx_file.path|.
+    CrxInstaller* installer = NULL;
+    if (service_->UpdateExtension(crx_file.id,
+                                  crx_file.path,
+                                  crx_file.download_url,
+                                  &installer)) {
+      crx_install_is_running_ = true;
+
+      // Source parameter ensures that we only see the completion event for the
+      // the installer we started.
+      registrar_.Add(this,
+                     chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+                     content::Source<CrxInstaller>(installer));
+    }
+    in_progress_ids_.erase(crx_file.id);
+    fetched_crx_files_.pop();
+  }
+
+  NotifyIfFinished();
+}
+
+void ExtensionUpdater::Observe(int type,
+                               const content::NotificationSource& source,
+                               const content::NotificationDetails& details) {
+  DCHECK(type == chrome::NOTIFICATION_CRX_INSTALLER_DONE);
+
+  // No need to listen for CRX_INSTALLER_DONE anymore.
+  registrar_.Remove(this,
+                    chrome::NOTIFICATION_CRX_INSTALLER_DONE,
+                    source);
+  crx_install_is_running_ = false;
+  // If any files are available to update, start one.
+  MaybeInstallCRXFile();
+}
+
+void ExtensionUpdater::NotifyStarted() {
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
+      content::Source<Profile>(profile_),
+      content::NotificationService::NoDetails());
+}
+
+void ExtensionUpdater::NotifyIfFinished() {
+  if (in_progress_ids_.empty()) {
+    VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
+    content::NotificationService::current()->Notify(
+        chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
+        content::Source<Profile>(profile_),
+        content::NotificationService::NoDetails());
+  }
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
new file mode 100644
index 0000000..53d4719
--- /dev/null
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
+#pragma once
+
+#include <set>
+#include <stack>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "googleurl/src/gurl.h"
+
+class ExtensionPrefs;
+class ExtensionServiceInterface;
+class PrefService;
+class Profile;
+
+namespace extensions {
+
+class ExtensionDownloader;
+class ExtensionUpdaterTest;
+
+// A class for doing auto-updates of installed Extensions. Used like this:
+//
+// ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service,
+//                                                  extension_prefs,
+//                                                  pref_service,
+//                                                  profile,
+//                                                  update_frequency_secs);
+// updater->Start();
+// ....
+// updater->Stop();
+class ExtensionUpdater : public ExtensionDownloaderDelegate,
+                         public content::NotificationObserver {
+ public:
+  // Holds a pointer to the passed |service|, using it for querying installed
+  // extensions and installing updated ones. The |frequency_seconds| parameter
+  // controls how often update checks are scheduled.
+  ExtensionUpdater(ExtensionServiceInterface* service,
+                   ExtensionPrefs* extension_prefs,
+                   PrefService* prefs,
+                   Profile* profile,
+                   int frequency_seconds);
+
+  virtual ~ExtensionUpdater();
+
+  // Starts the updater running.  Should be called at most once.
+  void Start();
+
+  // Stops the updater running, cancelling any outstanding update manifest and
+  // crx downloads. Does not cancel any in-progress installs.
+  void Stop();
+
+  // Posts a task to do an update check.  Does nothing if there is
+  // already a pending task that has not yet run.
+  void CheckSoon();
+
+  // Starts an update check right now, instead of waiting for the next
+  // regularly scheduled check or a pending check from CheckSoon().
+  void CheckNow();
+
+  // Set blacklist checks on or off.
+  void set_blacklist_checks_enabled(bool enabled) {
+    blacklist_checks_enabled_ = enabled;
+  }
+
+  // Returns true iff CheckSoon() has been called but the update check
+  // hasn't been performed yet.  This is used mostly by tests; calling
+  // code should just call CheckSoon().
+  bool WillCheckSoon() const;
+
+ private:
+  friend class ExtensionUpdaterTest;
+  friend class ExtensionUpdaterFileHandler;
+
+  // FetchedCRXFile holds information about a CRX file we fetched to disk,
+  // but have not yet installed.
+  struct FetchedCRXFile {
+    FetchedCRXFile();
+    FetchedCRXFile(const std::string& id,
+                   const FilePath& path,
+                   const GURL& download_url);
+    ~FetchedCRXFile();
+
+    std::string id;
+    FilePath path;
+    GURL download_url;
+  };
+
+  // Computes when to schedule the first update check.
+  base::TimeDelta DetermineFirstCheckDelay();
+
+  // Sets the timer to call TimerFired after roughly |target_delay| from now.
+  // To help spread load evenly on servers, this method adds some random
+  // jitter. It also saves the scheduled time so it can be reloaded on
+  // browser restart.
+  void ScheduleNextCheck(const base::TimeDelta& target_delay);
+
+  // BaseTimer::ReceiverMethod callback.
+  void TimerFired();
+
+  // Posted by CheckSoon().
+  void DoCheckSoon();
+
+  // Implenentation of ExtensionDownloaderDelegate.
+  virtual void OnExtensionDownloadFailed(const std::string& id,
+                                         Error error,
+                                         const PingResult& ping) OVERRIDE;
+
+  virtual void OnExtensionDownloadFinished(const std::string& id,
+                                           const FilePath& path,
+                                           const GURL& download_url,
+                                           const PingResult& ping) OVERRIDE;
+
+  virtual void OnBlacklistDownloadFinished(const std::string& data,
+                                           const std::string& package_hash,
+                                           const std::string& version,
+                                           const PingResult& ping) OVERRIDE;
+  virtual bool GetPingDataForExtension(
+      const std::string& id,
+      ManifestFetchData::PingData* ping_data) OVERRIDE;
+
+  virtual std::string GetUpdateUrlData(const std::string& id) OVERRIDE;
+
+  virtual bool IsExtensionPending(const std::string& id) OVERRIDE;
+
+  virtual bool GetExtensionExistingVersion(const std::string& id,
+                                           std::string* version) OVERRIDE;
+
+  void UpdatePingData(const std::string& id, const PingResult& ping_result);
+
+  // Starts installing a crx file that has been fetched but not installed yet.
+  void MaybeInstallCRXFile();
+
+  // content::NotificationObserver implementation.
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  // Send a notification that update checks are starting.
+  void NotifyStarted();
+
+  // Send a notification if we're finished updating.
+  void NotifyIfFinished();
+
+  // Whether Start() has been called but not Stop().
+  bool alive_;
+
+  base::WeakPtrFactory<ExtensionUpdater> weak_ptr_factory_;
+
+  // Pointer back to the service that owns this ExtensionUpdater.
+  ExtensionServiceInterface* service_;
+
+  // Fetches the crx files for the extensions that have an available update.
+  scoped_ptr<ExtensionDownloader> downloader_;
+
+  base::OneShotTimer<ExtensionUpdater> timer_;
+  int frequency_seconds_;
+  bool will_check_soon_;
+
+  ExtensionPrefs* extension_prefs_;
+  PrefService* prefs_;
+  Profile* profile_;
+  bool blacklist_checks_enabled_;
+
+  // The ids of extensions that have in-progress update checks.
+  std::set<std::string> in_progress_ids_;
+
+  // Observes CRX installs we initiate.
+  content::NotificationRegistrar registrar_;
+
+  // True when a CrxInstaller is doing an install.  Used in MaybeUpdateCrxFile()
+  // to keep more than one install from running at once.
+  bool crx_install_is_running_;
+
+  // Fetched CRX files waiting to be installed.
+  std::stack<FetchedCRXFile> fetched_crx_files_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_EXTENSION_UPDATER_H_
diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
similarity index 63%
rename from chrome/browser/extensions/extension_updater_unittest.cc
rename to chrome/browser/extensions/updater/extension_updater_unittest.cc
index b7cdcce..825921d 100644
--- a/chrome/browser/extensions/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include <map>
+#include <set>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/file_util.h"
@@ -18,32 +20,52 @@
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_error_reporter.h"
 #include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/extensions/test_extension_prefs.h"
 #include "chrome/browser/extensions/test_extension_service.h"
+#include "chrome/browser/extensions/updater/extension_downloader.h"
+#include "chrome/browser/extensions/updater/extension_downloader_delegate.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
 #include "chrome/browser/google/google_util.h"
 #include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/chrome_notification_types.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
 #include "content/test/test_browser_thread.h"
 #include "content/test/test_url_fetcher_factory.h"
 #include "libxml/globals.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::Time;
 using base::TimeDelta;
 using content::BrowserThread;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::_;
+
+namespace extensions {
+
+typedef ExtensionDownloaderDelegate::Error Error;
+typedef ExtensionDownloaderDelegate::PingResult PingResult;
 
 namespace {
 
 const char kEmptyUpdateUrlData[] = "";
 
-int expected_load_flags =
+int kExpectedLoadFlags =
     net::LOAD_DO_NOT_SEND_COOKIES |
     net::LOAD_DO_NOT_SAVE_COOKIES |
     net::LOAD_DISABLE_CACHE;
@@ -51,13 +73,94 @@
 const ManifestFetchData::PingData kNeverPingedData(
     ManifestFetchData::kNeverPinged, ManifestFetchData::kNeverPinged);
 
+class MockExtensionDownloaderDelegate : public ExtensionDownloaderDelegate {
+ public:
+  MOCK_METHOD3(OnExtensionDownloadFailed,
+               void(const std::string&, Error, const PingResult&));
+  MOCK_METHOD4(OnExtensionDownloadFinished, void(const std::string&,
+                                                 const FilePath&,
+                                                 const GURL&,
+                                                 const PingResult&));
+  MOCK_METHOD4(OnBlacklistDownloadFinished, void(const std::string&,
+                                                 const std::string&,
+                                                 const std::string&,
+                                                 const PingResult&));
+  MOCK_METHOD2(GetPingDataForExtension,
+               bool(const std::string&, ManifestFetchData::PingData*));
+  MOCK_METHOD1(GetUpdateUrlData, std::string(const std::string&));
+  MOCK_METHOD1(IsExtensionPending, bool(const std::string&));
+  MOCK_METHOD2(GetExtensionExistingVersion,
+               bool(const std::string&, std::string*));
+};
+
+const int kNotificationsObserved[] = {
+  chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
+  chrome::NOTIFICATION_EXTENSION_UPDATING_FINISHED,
+  chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
+};
+
+// A class that observes the notifications sent by the ExtensionUpdater and
+// the ExtensionDownloader.
+class NotificationsObserver : public content::NotificationObserver {
+ public:
+  NotificationsObserver() {
+    for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+      count_[i] = 0;
+      registrar_.Add(this,
+                     kNotificationsObserved[i],
+                     content::NotificationService::AllSources());
+    }
+  }
+
+  virtual ~NotificationsObserver() {
+    for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+      registrar_.Remove(this,
+                        kNotificationsObserved[i],
+                        content::NotificationService::AllSources());
+    }
+  }
+
+  size_t StartedCount() { return count_[0]; }
+  size_t FinishedCount() { return count_[1]; }
+  size_t UpdatedCount() { return count_[2]; }
+
+  bool Updated(const std::string& id) {
+    return updated_.find(id) != updated_.end();
+  }
+
+ private:
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE {
+    for (size_t i = 0; i < arraysize(kNotificationsObserved); ++i) {
+      if (kNotificationsObserved[i] == type) {
+        count_[i]++;
+        if (type == chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND) {
+          updated_.insert(
+              *(content::Details<const std::string>(details).ptr()));
+        }
+        return;
+      }
+    }
+    NOTREACHED();
+  }
+
+  content::NotificationRegistrar registrar_;
+  size_t count_[arraysize(kNotificationsObserved)];
+  std::set<std::string> updated_;
+
+  DISALLOW_COPY_AND_ASSIGN(NotificationsObserver);
+};
+
 }  // namespace
 
 // Base class for further specialized test classes.
 class MockService : public TestExtensionService {
  public:
   MockService()
-      : pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {}
+      : pending_extension_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(*this)) {
+    profile_.CreateRequestContext();
+  }
   virtual ~MockService() {}
 
   virtual PendingExtensionManager* pending_extension_manager() OVERRIDE {
@@ -68,6 +171,10 @@
 
   Profile* profile() { return &profile_; }
 
+  net::URLRequestContextGetter* request_context() {
+    return profile_.GetRequestContext();
+  }
+
   ExtensionPrefs* extension_prefs() { return prefs_.prefs(); }
 
   PrefService* pref_service() { return prefs_.pref_service(); }
@@ -287,24 +394,33 @@
 // inside this class (which is a friend to ExtensionUpdater).
 class ExtensionUpdaterTest : public testing::Test {
  public:
-  static void SimulateTimerFired(ExtensionUpdater* updater) {
+  ExtensionUpdaterTest()
+      : ui_thread_(BrowserThread::UI, &loop_),
+        file_thread_(BrowserThread::FILE, &loop_),
+        io_thread_(BrowserThread::IO, &loop_) {}
+
+  virtual void TearDown() OVERRIDE {
+    // Some tests create URLRequestContextGetters, whose destruction must run
+    // on the IO thread. Make sure the IO loop spins before shutdown so that
+    // those objects are released.
+    loop_.RunAllPending();
+  }
+
+  void RunAllPending() {
+    loop_.RunAllPending();
+  }
+
+  void SimulateTimerFired(ExtensionUpdater* updater) {
     EXPECT_TRUE(updater->timer_.IsRunning());
     updater->timer_.Stop();
     updater->TimerFired();
   }
 
-  static void SimulateCheckSoon(const ExtensionUpdater& updater,
-                                MessageLoop* message_loop) {
-    EXPECT_TRUE(updater.will_check_soon_);
-    message_loop->RunAllPending();
-  }
-
   // Adds a Result with the given data to results.
-  static void AddParseResult(
-      const std::string& id,
-      const std::string& version,
-      const std::string& url,
-      UpdateManifest::Results* results) {
+  void AddParseResult(const std::string& id,
+                      const std::string& version,
+                      const std::string& url,
+                      UpdateManifest::Results* results) {
     UpdateManifest::Result result;
     result.extension_id = id;
     result.version = version;
@@ -312,13 +428,23 @@
     results->list.push_back(result);
   }
 
-  static void TestExtensionUpdateCheckRequests(bool pending) {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
+  void ResetDownloader(ExtensionUpdater* updater,
+                       ExtensionDownloader* downloader) {
+    EXPECT_FALSE(updater->downloader_.get());
+    updater->downloader_.reset(downloader);
+  }
 
+  void StartUpdateCheck(ExtensionDownloader* downloader,
+                        ManifestFetchData* fetch_data) {
+    downloader->StartUpdateCheck(fetch_data);
+  }
+
+  size_t ManifestFetchersCount(ExtensionDownloader* downloader) {
+    return downloader->manifests_pending_.size() +
+           (downloader->manifest_fetcher_.get() ? 1 : 0);
+  }
+
+  void TestExtensionUpdateCheckRequests(bool pending) {
     // Create an extension with an update_url.
     ServiceForManifestTests service;
     std::string update_url("http://foo.com/bar");
@@ -349,7 +475,7 @@
 
     // Get the url our mock fetcher was asked to fetch.
     TestURLFetcher* fetcher =
-        factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+        factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     const GURL& url = fetcher->GetOriginalURL();
     EXPECT_FALSE(url.is_empty());
     EXPECT_TRUE(url.is_valid());
@@ -378,14 +504,9 @@
     EXPECT_EQ("", params["uc"]);
   }
 
-  static void TestBlacklistUpdateCheckRequests() {
-    ServiceForManifestTests service;
-
+  void TestBlacklistUpdateCheckRequests() {
     // Setup and start the updater.
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
+    ServiceForManifestTests service;
 
     TestURLFetcherFactory factory;
     ExtensionUpdater updater(
@@ -398,7 +519,7 @@
 
     // Get the url our mock fetcher was asked to fetch.
     TestURLFetcher* fetcher =
-        factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+        factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     ASSERT_FALSE(fetcher == NULL);
     const GURL& url = fetcher->GetOriginalURL();
 
@@ -425,142 +546,138 @@
     EXPECT_TRUE(ContainsKey(params, "ping"));
   }
 
-  static void TestUpdateUrlDataEmpty() {
+  void TestUpdateUrlDataEmpty() {
     const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     const std::string version = "1.0";
 
     // Make sure that an empty update URL data string does not cause a ap=
     // option to appear in the x= parameter.
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
-    fetch_data.AddExtension(id, version,
-                            kNeverPingedData, "", "");
+    fetch_data.AddExtension(id, version, &kNeverPingedData, "", "");
     EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
               "%26v%3D1.0%26uc",
               fetch_data.full_url().spec());
   }
 
-  static void TestUpdateUrlDataSimple() {
+  void TestUpdateUrlDataSimple() {
     const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     const std::string version = "1.0";
 
     // Make sure that an update URL data string causes an appropriate ap=
     // option to appear in the x= parameter.
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
-    fetch_data.AddExtension(id, version,
-                            kNeverPingedData, "bar", "");
+    fetch_data.AddExtension(id, version, &kNeverPingedData, "bar", "");
     EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
               "%26v%3D1.0%26uc%26ap%3Dbar",
               fetch_data.full_url().spec());
   }
 
-  static void TestUpdateUrlDataCompound() {
+  void TestUpdateUrlDataCompound() {
     const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     const std::string version = "1.0";
 
     // Make sure that an update URL data string causes an appropriate ap=
     // option to appear in the x= parameter.
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
-    fetch_data.AddExtension(id, version,
-                            kNeverPingedData, "a=1&b=2&c", "");
+    fetch_data.AddExtension(id, version, &kNeverPingedData, "a=1&b=2&c", "");
     EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
               "%26v%3D1.0%26uc%26ap%3Da%253D1%2526b%253D2%2526c",
               fetch_data.full_url().spec());
   }
 
-  static void TestUpdateUrlDataFromGallery(const std::string& gallery_url) {
+  void TestUpdateUrlDataFromGallery(const std::string& gallery_url) {
+    TestURLFetcherFactory factory;
+
     MockService service;
-    ManifestFetchesBuilder builder(&service, service.extension_prefs());
+    MockExtensionDownloaderDelegate delegate;
+    ExtensionDownloader downloader(&delegate, service.request_context());
     ExtensionList extensions;
     std::string url(gallery_url);
 
     service.CreateTestExtensions(1, 1, &extensions, &url, Extension::INTERNAL);
-    builder.AddExtension(*extensions[0]);
-    std::vector<ManifestFetchData*> fetches = builder.GetFetches();
-    EXPECT_EQ(1u, fetches.size());
-    scoped_ptr<ManifestFetchData> fetch(fetches[0]);
-    fetches.clear();
 
+    const std::string& id = extensions[0]->id();
+    EXPECT_CALL(delegate, GetPingDataForExtension(id, _));
+
+    downloader.AddExtension(*extensions[0]);
+    downloader.StartAllPending();
+    TestURLFetcher* fetcher =
+        factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+    ASSERT_TRUE(fetcher);
     // Make sure that extensions that update from the gallery ignore any
     // update URL data.
-    const std::string& update_url = fetch->full_url().spec();
+    const std::string& update_url = fetcher->GetOriginalURL().spec();
     std::string::size_type x = update_url.find("x=");
     EXPECT_NE(std::string::npos, x);
     std::string::size_type ap = update_url.find("ap%3D", x);
     EXPECT_EQ(std::string::npos, ap);
   }
 
-  static void TestInstallSource() {
+  void TestInstallSource() {
     const std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     const std::string version = "1.0";
     const std::string install_source = "instally";
 
     // Make sure that an installsource= appears in the x= parameter.
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
-    fetch_data.AddExtension(id, version, kNeverPingedData,
+    fetch_data.AddExtension(id, version, &kNeverPingedData,
                             kEmptyUpdateUrlData, install_source);
     EXPECT_EQ("http://localhost/foo\?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
               "%26v%3D1.0%26installsource%3Dinstally%26uc",
               fetch_data.full_url().spec());
   }
 
-  static void TestDetermineUpdates() {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
-    // Create a set of test extensions
-    ServiceForManifestTests service;
-    ExtensionList tmp;
-    service.CreateTestExtensions(1, 3, &tmp, NULL, Extension::INTERNAL);
-    service.set_extensions(tmp);
-
-    ExtensionUpdater updater(
-        &service, service.extension_prefs(), service.pref_service(),
-        service.profile(), kUpdateFrequencySecs);
-    updater.Start();
+  void TestDetermineUpdates() {
+    TestingProfile profile;
+    profile.CreateRequestContext();
+    MockExtensionDownloaderDelegate delegate;
+    ExtensionDownloader downloader(&delegate, profile.GetRequestContext());
 
     // Check passing an empty list of parse results to DetermineUpdates
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
     UpdateManifest::Results updates;
-    std::vector<int> updateable = updater.DetermineUpdates(fetch_data,
-                                                           updates);
+    std::vector<int> updateable;
+    downloader.DetermineUpdates(fetch_data, updates, &updateable);
     EXPECT_TRUE(updateable.empty());
 
     // Create two updates - expect that DetermineUpdates will return the first
     // one (v1.0 installed, v1.1 available) but not the second one (both
     // installed and available at v2.0).
-    scoped_ptr<Version> one(Version::GetVersionFromString("1.0"));
-    EXPECT_TRUE(tmp[0]->version()->Equals(*one));
-    fetch_data.AddExtension(tmp[0]->id(), tmp[0]->VersionString(),
-                            kNeverPingedData,
-                            kEmptyUpdateUrlData,
-                            "");
-    AddParseResult(tmp[0]->id(),
-        "1.1", "http://localhost/e1_1.1.crx", &updates);
-    fetch_data.AddExtension(tmp[1]->id(), tmp[1]->VersionString(),
-                            kNeverPingedData,
-                            kEmptyUpdateUrlData,
-                            "");
-    AddParseResult(tmp[1]->id(),
-        tmp[1]->VersionString(), "http://localhost/e2_2.0.crx", &updates);
-    updateable = updater.DetermineUpdates(fetch_data, updates);
+    const std::string id1 = GenerateId("1");
+    const std::string id2 = GenerateId("2");
+    fetch_data.AddExtension(id1, "1.0.0.0",
+                            &kNeverPingedData, kEmptyUpdateUrlData, "");
+    AddParseResult(id1, "1.1",
+                   "http://localhost/e1_1.1.crx", &updates);
+    fetch_data.AddExtension(id2, "2.0.0.0",
+                            &kNeverPingedData, kEmptyUpdateUrlData, "");
+    AddParseResult(id2, "2.0.0.0",
+                   "http://localhost/e2_2.0.crx", &updates);
+
+    EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(false));
+    EXPECT_CALL(delegate, GetExtensionExistingVersion(id1, _))
+        .WillOnce(DoAll(SetArgPointee<1>("1.0.0.0"),
+                        Return(true)));
+    EXPECT_CALL(delegate, GetExtensionExistingVersion(id2, _))
+        .WillOnce(DoAll(SetArgPointee<1>("2.0.0.0"),
+                        Return(true)));
+
+    downloader.DetermineUpdates(fetch_data, updates, &updateable);
     EXPECT_EQ(1u, updateable.size());
     EXPECT_EQ(0, updateable[0]);
   }
 
-  static void TestDetermineUpdatesPending() {
+  void TestDetermineUpdatesPending() {
     // Create a set of test extensions
     ServiceForManifestTests service;
     PendingExtensionManager* pending_extension_manager =
         service.pending_extension_manager();
     SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager);
 
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    ExtensionUpdater updater(
-        &service, service.extension_prefs(), service.pref_service(),
-        service.profile(), kUpdateFrequencySecs);
-    updater.Start();
+    TestingProfile profile;
+    profile.CreateRequestContext();
+    MockExtensionDownloaderDelegate delegate;
+    ExtensionDownloader downloader(&delegate, profile.GetRequestContext());
 
     ManifestFetchData fetch_data(GURL("http://localhost/foo"));
     UpdateManifest::Results updates;
@@ -572,15 +689,17 @@
     std::set<std::string>::const_iterator it;
     for (it = ids_for_update_check.begin();
          it != ids_for_update_check.end(); ++it) {
-      fetch_data.AddExtension(*it,
-                              "1.0.0.0",
-                              kNeverPingedData,
-                              kEmptyUpdateUrlData,
-                              "");
+      fetch_data.AddExtension(*it, "1.0.0.0",
+                              &kNeverPingedData, kEmptyUpdateUrlData, "");
       AddParseResult(*it, "1.1", "http://localhost/e1_1.1.crx", &updates);
     }
-    std::vector<int> updateable =
-        updater.DetermineUpdates(fetch_data, updates);
+
+    // The delegate will tell the downloader that all the extensions are
+    // pending.
+    EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(true));
+
+    std::vector<int> updateable;
+    downloader.DetermineUpdates(fetch_data, updates, &updateable);
     // All the apps should be updateable.
     EXPECT_EQ(3u, updateable.size());
     for (std::vector<int>::size_type i = 0; i < updateable.size(); ++i) {
@@ -588,93 +707,120 @@
     }
   }
 
-  static void TestMultipleManifestDownloading() {
-    MessageLoop ui_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &ui_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE);
-    file_thread.Start();
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
-
+  void TestMultipleManifestDownloading() {
     TestURLFetcherFactory factory;
     TestURLFetcher* fetcher = NULL;
-    scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests);
-    ExtensionUpdater updater(service.get(),
-                             service->extension_prefs(),
-                             service->pref_service(),
-                             service->profile(),
-                             kUpdateFrequencySecs);
-    updater.Start();
+    NotificationsObserver observer;
+    MockService service;
+    MockExtensionDownloaderDelegate delegate;
+    ExtensionDownloader downloader(&delegate, service.request_context());
 
-    GURL url1("http://localhost/manifest1");
-    GURL url2("http://localhost/manifest2");
+    GURL kUpdateUrl("http://localhost/manifest1");
 
-    // Request 2 update checks - the first should begin immediately and the
-    // second one should be queued up.
-    ManifestFetchData* fetch1 = new ManifestFetchData(url1);
-    ManifestFetchData* fetch2 = new ManifestFetchData(url2);
+    ManifestFetchData* fetch1 = new ManifestFetchData(kUpdateUrl);
+    ManifestFetchData* fetch2 = new ManifestFetchData(kUpdateUrl);
+    ManifestFetchData* fetch3 = new ManifestFetchData(kUpdateUrl);
+    ManifestFetchData* fetch4 = new ManifestFetchData(kUpdateUrl);
     ManifestFetchData::PingData zeroDays(0, 0);
-    fetch1->AddExtension("1111", "1.0", zeroDays, kEmptyUpdateUrlData, "");
-    fetch2->AddExtension("12345", "2.0", kNeverPingedData,
-                         kEmptyUpdateUrlData, "");
-    updater.StartUpdateCheck(fetch1);
-    updater.StartUpdateCheck(fetch2);
+    fetch1->AddExtension("1111", "1.0", &zeroDays, kEmptyUpdateUrlData, "");
+    fetch2->AddExtension("2222", "2.0", &zeroDays, kEmptyUpdateUrlData, "");
+    fetch3->AddExtension("3333", "3.0", &zeroDays, kEmptyUpdateUrlData, "");
+    fetch4->AddExtension("4444", "4.0", &zeroDays, kEmptyUpdateUrlData, "");
 
-    std::string invalid_xml = "invalid xml";
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+    // This will start the first fetcher and queue the others. The next in queue
+    // is started as each fetcher receives its response.
+    downloader.StartUpdateCheck(fetch1);
+    downloader.StartUpdateCheck(fetch2);
+    downloader.StartUpdateCheck(fetch3);
+    downloader.StartUpdateCheck(fetch4);
+    RunAllPending();
+
+    // The first fetch will fail.
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+    EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+        "1111", ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED, _));
+    fetcher->set_url(kUpdateUrl);
+    fetcher->set_status(net::URLRequestStatus());
+    fetcher->set_response_code(400);
+    fetcher->delegate()->OnURLFetchComplete(fetcher);
+    RunAllPending();
+    Mock::VerifyAndClearExpectations(&delegate);
 
-    fetcher->set_url(url1);
+    // The second fetch gets invalid data.
+    const std::string kInvalidXml = "invalid xml";
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+    EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+    EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+        "2222", ExtensionDownloaderDelegate::MANIFEST_INVALID, _));
+    fetcher->set_url(kUpdateUrl);
     fetcher->set_status(net::URLRequestStatus());
     fetcher->set_response_code(200);
-    fetcher->SetResponseString(invalid_xml);
+    fetcher->SetResponseString(kInvalidXml);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
+    RunAllPending();
+    Mock::VerifyAndClearExpectations(&delegate);
 
-    // Now that the first request is complete, make sure the second one has
-    // been started.
-    const std::string kValidXml =
+    // The third fetcher doesn't have an update available.
+    const std::string kNoUpdate =
         "<?xml version='1.0' encoding='UTF-8'?>"
         "<gupdate xmlns='http://www.google.com/update2/response'"
         "                protocol='2.0'>"
-        " <app appid='12345'>"
-        "  <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
-        "               version='1.2.3.4' prodversionmin='2.0.143.0' />"
+        " <app appid='3333'>"
+        "  <updatecheck codebase='http://example.com/extension_3.0.0.0.crx'"
+        "               version='3.0.0.0' prodversionmin='3.0.0.0' />"
         " </app>"
         "</gupdate>";
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
-
-    fetcher->set_url(url2);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+    EXPECT_CALL(delegate, IsExtensionPending("3333")).WillOnce(Return(false));
+    EXPECT_CALL(delegate, GetExtensionExistingVersion("3333", _))
+        .WillOnce(DoAll(SetArgPointee<1>("3.0.0.0"),
+                        Return(true)));
+    EXPECT_CALL(delegate, OnExtensionDownloadFailed(
+        "3333", ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE, _));
+    fetcher->set_url(kUpdateUrl);
     fetcher->set_status(net::URLRequestStatus());
     fetcher->set_response_code(200);
-    fetcher->SetResponseString(kValidXml);
+    fetcher->SetResponseString(kNoUpdate);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
+    RunAllPending();
+    Mock::VerifyAndClearExpectations(&delegate);
 
-    // This should run the manifest parsing, then we want to make sure that our
-    // service was called with GetExtensionById with the matching id from
-    // kValidXml.
-    file_thread.Stop();
-    io_thread.Stop();
-    ui_loop.RunAllPending();
-    EXPECT_EQ("12345", service->last_inquired_extension_id());
-    xmlCleanupGlobals();
+    // The last fetcher has an update.
+    const std::string kUpdateAvailable =
+        "<?xml version='1.0' encoding='UTF-8'?>"
+        "<gupdate xmlns='http://www.google.com/update2/response'"
+        "                protocol='2.0'>"
+        " <app appid='4444'>"
+        "  <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
+        "               version='4.0.42.0' prodversionmin='4.0.42.0' />"
+        " </app>"
+        "</gupdate>";
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+    EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
+    EXPECT_CALL(delegate, IsExtensionPending("4444")).WillOnce(Return(false));
+    EXPECT_CALL(delegate, GetExtensionExistingVersion("4444", _))
+        .WillOnce(DoAll(SetArgPointee<1>("4.0.0.0"),
+                        Return(true)));
+    fetcher->set_url(kUpdateUrl);
+    fetcher->set_status(net::URLRequestStatus());
+    fetcher->set_response_code(200);
+    fetcher->SetResponseString(kUpdateAvailable);
+    fetcher->delegate()->OnURLFetchComplete(fetcher);
+    RunAllPending();
+    Mock::VerifyAndClearExpectations(&delegate);
 
-    // The FILE thread is needed for |service|'s cleanup,
-    // because of ImportantFileWriter.
-    file_thread.Start();
-    service.reset();
+    // Verify that the downloader decided to update this extension.
+    EXPECT_EQ(1u, observer.UpdatedCount());
+    EXPECT_TRUE(observer.Updated("4444"));
   }
 
-  static void TestSingleExtensionDownloading(bool pending) {
-    MessageLoop ui_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &ui_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE);
-    file_thread.Start();
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
-
+  void TestSingleExtensionDownloading(bool pending) {
     TestURLFetcherFactory factory;
     TestURLFetcher* fetcher = NULL;
     scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests);
@@ -683,6 +829,9 @@
                              service->profile(),
                              kUpdateFrequencySecs);
     updater.Start();
+    ResetDownloader(
+        &updater,
+        new ExtensionDownloader(&updater, service->request_context()));
 
     GURL test_url("http://localhost/extension.crx");
 
@@ -690,7 +839,8 @@
     std::string hash = "";
     scoped_ptr<Version> version(Version::GetVersionFromString("0.0.1"));
     ASSERT_TRUE(version.get());
-    updater.FetchUpdatedExtension(id, test_url, hash, version->GetString());
+    updater.downloader_->FetchUpdatedExtension(
+        id, test_url, hash, version->GetString());
 
     if (pending) {
       const bool kIsFromSync = true;
@@ -706,9 +856,9 @@
 
     // Call back the ExtensionUpdater with a 200 response and some test data
     FilePath extension_file_path(FILE_PATH_LITERAL("/whatever"));
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
 
     fetcher->set_url(test_url);
     fetcher->set_status(net::URLRequestStatus());
@@ -716,8 +866,7 @@
     fetcher->SetResponseFilePath(extension_file_path);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
 
-    file_thread.Stop();
-    ui_loop.RunAllPending();
+    RunAllPending();
 
     // Expect that ExtensionUpdater asked the mock extensions service to install
     // a file with the test data for the right id.
@@ -726,20 +875,9 @@
     EXPECT_FALSE(tmpfile_path.empty());
     EXPECT_EQ(test_url, service->download_url());
     EXPECT_EQ(extension_file_path, tmpfile_path);
-
-    // The FILE thread is needed for |service|'s cleanup,
-    // because of ImportantFileWriter.
-    file_thread.Start();
-    service.reset();
   }
 
-  static void TestBlacklistDownloading() {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
-
+  void TestBlacklistDownloading() {
     TestURLFetcherFactory factory;
     TestURLFetcher* fetcher = NULL;
     ServiceForBlacklistTests service;
@@ -747,6 +885,9 @@
         &service, service.extension_prefs(), service.pref_service(),
         service.profile(), kUpdateFrequencySecs);
     updater.Start();
+    ResetDownloader(
+        &updater,
+        new ExtensionDownloader(&updater, service.request_context()));
     GURL test_url("http://localhost/extension.crx");
 
     std::string id = "com.google.crx.blacklist";
@@ -755,14 +896,14 @@
         "2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C";
 
     std::string version = "0.0.1";
-    updater.FetchUpdatedExtension(id, test_url, hash, version);
+    updater.downloader_->FetchUpdatedExtension(id, test_url, hash, version);
 
     // Call back the ExtensionUpdater with a 200 response and some test data.
     std::string extension_data("aaabbb");
 
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
 
     fetcher->set_url(test_url);
     fetcher->set_status(net::URLRequestStatus());
@@ -770,7 +911,7 @@
     fetcher->SetResponseString(extension_data);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
 
-    message_loop.RunAllPending();
+    RunAllPending();
 
     // The updater should have called extension service to process the
     // blacklist.
@@ -784,13 +925,7 @@
   // mock extensions service has UpdateExtension(...) return true, and
   // the test is responsible for creating fake CrxInstallers.  Otherwise,
   // UpdateExtension() returns false, signaling install failures.
-  static void TestMultipleExtensionDownloading(bool updates_start_running) {
-    MessageLoopForUI message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-    content::TestBrowserThread io_thread(BrowserThread::IO);
-    io_thread.Start();
-
+  void TestMultipleExtensionDownloading(bool updates_start_running) {
     TestURLFetcherFactory factory;
     TestURLFetcher* fetcher = NULL;
     ServiceForDownloadTests service;
@@ -798,6 +933,9 @@
         &service, service.extension_prefs(), service.pref_service(),
         service.profile(), kUpdateFrequencySecs);
     updater.Start();
+    ResetDownloader(
+        &updater,
+        new ExtensionDownloader(&updater, service.request_context()));
 
     EXPECT_FALSE(updater.crx_install_is_running_);
 
@@ -813,15 +951,15 @@
     std::string version1 = "0.1";
     std::string version2 = "0.1";
     // Start two fetches
-    updater.FetchUpdatedExtension(id1, url1, hash1, version1);
-    updater.FetchUpdatedExtension(id2, url2, hash2, version2);
+    updater.downloader_->FetchUpdatedExtension(id1, url1, hash1, version1);
+    updater.downloader_->FetchUpdatedExtension(id2, url2, hash2, version2);
 
     // Make the first fetch complete.
     FilePath extension_file_path(FILE_PATH_LITERAL("/whatever"));
 
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
 
     // We need some CrxInstallers, and CrxInstallers require a real
     // ExtensionService.  Create one on the testing profile.  Any action
@@ -856,28 +994,28 @@
     fetcher->SetResponseFilePath(extension_file_path);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
 
-    message_loop.RunAllPending();
+    RunAllPending();
 
     // Expect that the service was asked to do an install with the right data.
     FilePath tmpfile_path = service.install_path();
     EXPECT_FALSE(tmpfile_path.empty());
     EXPECT_EQ(id1, service.extension_id());
     EXPECT_EQ(url1, service.download_url());
-    message_loop.RunAllPending();
+    RunAllPending();
 
     // Make sure the second fetch finished and asked the service to do an
     // update.
     FilePath extension_file_path2(FILE_PATH_LITERAL("/whatever2"));
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kExtensionFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
-    EXPECT_TRUE(fetcher->GetLoadFlags() == expected_load_flags);
+    EXPECT_TRUE(fetcher->GetLoadFlags() == kExpectedLoadFlags);
 
     fetcher->set_url(url2);
     fetcher->set_status(net::URLRequestStatus());
     fetcher->set_response_code(200);
     fetcher->SetResponseFilePath(extension_file_path2);
     fetcher->delegate()->OnURLFetchComplete(fetcher);
-    message_loop.RunAllPending();
+    RunAllPending();
 
     if (updates_start_running) {
       EXPECT_TRUE(updater.crx_install_is_running_);
@@ -908,7 +1046,7 @@
     EXPECT_FALSE(updater.crx_install_is_running_);
   }
 
-  static void TestGalleryRequestsWithBrand(bool use_organic_brand_code) {
+  void TestGalleryRequestsWithBrand(bool use_organic_brand_code) {
     google_util::BrandForTesting brand_for_testing(
         use_organic_brand_code ? "GGLS" : "TEST");
 
@@ -930,9 +1068,8 @@
             continue;
 
           bool active_bit = k > 0;
-          ExtensionUpdaterTest::TestGalleryRequests(
-              rollcall_ping_days, active_ping_days, active_bit,
-              !use_organic_brand_code);
+          TestGalleryRequests(rollcall_ping_days, active_ping_days, active_bit,
+                              !use_organic_brand_code);
           ASSERT_FALSE(HasFailure()) <<
             " rollcall_ping_days=" << ping_cases[i] <<
             " active_ping_days=" << ping_cases[j] <<
@@ -948,14 +1085,10 @@
   // value should be present at most once per day, and can be calculated based
   // on the delta between now and the last ping time (or in the case of active
   // pings, that delta plus whether the app has been active).
-  static void TestGalleryRequests(int rollcall_ping_days,
-                                  int active_ping_days,
-                                  bool active_bit,
-                                  bool expect_brand_code) {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
+  void TestGalleryRequests(int rollcall_ping_days,
+                           int active_ping_days,
+                           bool active_bit,
+                           bool expect_brand_code) {
     TestURLFetcherFactory factory;
 
     // Set up 2 mock extensions, one with a google.com update url and one
@@ -998,15 +1131,15 @@
     ExtensionUpdater updater(
         &service, service.extension_prefs(), service.pref_service(),
         service.profile(), kUpdateFrequencySecs);
-    updater.Start();
     updater.set_blacklist_checks_enabled(false);
+    updater.Start();
+    updater.CheckNow();
 
     // Make the updater do manifest fetching, and note the urls it tries to
     // fetch.
     std::vector<GURL> fetched_urls;
-    updater.CheckNow();
     TestURLFetcher* fetcher =
-      factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+      factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL);
     fetched_urls.push_back(fetcher->GetOriginalURL());
 
@@ -1016,7 +1149,7 @@
     fetcher->SetResponseString("");
     fetcher->delegate()->OnURLFetchComplete(fetcher);
 
-    fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
+    fetcher = factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
     fetched_urls.push_back(fetcher->GetOriginalURL());
 
     // The urls could have been fetched in either order, so use the host to
@@ -1078,37 +1211,42 @@
   // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is
   // the first time we fetched the extension, or 2) We sent a ping value of
   // >= 1 day for the extension.
-  static void TestHandleManifestResults() {
+  void TestHandleManifestResults() {
     ServiceForManifestTests service;
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    ExtensionUpdater updater(
-        &service, service.extension_prefs(), service.pref_service(),
-        service.profile(), kUpdateFrequencySecs);
-    updater.Start();
-
     GURL update_url("http://www.google.com/manifest");
     ExtensionList tmp;
     service.CreateTestExtensions(1, 1, &tmp, &update_url.spec(),
                                  Extension::INTERNAL);
     service.set_extensions(tmp);
 
+    ExtensionUpdater updater(
+        &service, service.extension_prefs(), service.pref_service(),
+        service.profile(), kUpdateFrequencySecs);
+    updater.Start();
+    ResetDownloader(
+        &updater,
+        new ExtensionDownloader(&updater, service.request_context()));
+
     ManifestFetchData fetch_data(update_url);
     const Extension* extension = tmp[0];
     fetch_data.AddExtension(extension->id(), extension->VersionString(),
-                            kNeverPingedData,
-                            kEmptyUpdateUrlData,
-                            "");
+                            &kNeverPingedData, kEmptyUpdateUrlData, "");
     UpdateManifest::Results results;
     results.daystart_elapsed_seconds = 750;
 
-    updater.HandleManifestResults(fetch_data, &results);
+    updater.downloader_->HandleManifestResults(fetch_data, &results);
     Time last_ping_day =
         service.extension_prefs()->LastPingDay(extension->id());
     EXPECT_FALSE(last_ping_day.is_null());
     int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds();
     EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5);
   }
+
+ private:
+  MessageLoop loop_;
+  content::TestBrowserThread ui_thread_;
+  content::TestBrowserThread file_thread_;
+  content::TestBrowserThread io_thread_;
 };
 
 // Because we test some private methods of ExtensionUpdater, it's easer for the
@@ -1116,169 +1254,191 @@
 // subclasses where friendship with ExtenionUpdater is not inherited.
 
 TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequests) {
-  ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(false);
+  TestExtensionUpdateCheckRequests(false);
 }
 
 TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequestsPending) {
-  ExtensionUpdaterTest::TestExtensionUpdateCheckRequests(true);
+  TestExtensionUpdateCheckRequests(true);
 }
 
 TEST_F(ExtensionUpdaterTest, TestBlacklistUpdateCheckRequests) {
-  ExtensionUpdaterTest::TestBlacklistUpdateCheckRequests();
+  TestBlacklistUpdateCheckRequests();
 }
 
 TEST_F(ExtensionUpdaterTest, TestUpdateUrlData) {
-  MessageLoop message_loop;
-  content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
-  ExtensionUpdaterTest::TestUpdateUrlDataEmpty();
-  ExtensionUpdaterTest::TestUpdateUrlDataSimple();
-  ExtensionUpdaterTest::TestUpdateUrlDataCompound();
-  ExtensionUpdaterTest::TestUpdateUrlDataFromGallery(
+  TestUpdateUrlDataEmpty();
+  TestUpdateUrlDataSimple();
+  TestUpdateUrlDataCompound();
+  TestUpdateUrlDataFromGallery(
       extension_urls::GetWebstoreUpdateUrl(false).spec());
-  ExtensionUpdaterTest::TestUpdateUrlDataFromGallery(
+  TestUpdateUrlDataFromGallery(
       extension_urls::GetWebstoreUpdateUrl(true).spec());
 }
 
 TEST_F(ExtensionUpdaterTest, TestInstallSource) {
-  ExtensionUpdaterTest::TestInstallSource();
+  TestInstallSource();
 }
 
 TEST_F(ExtensionUpdaterTest, TestDetermineUpdates) {
-  ExtensionUpdaterTest::TestDetermineUpdates();
+  TestDetermineUpdates();
 }
 
 TEST_F(ExtensionUpdaterTest, TestDetermineUpdatesPending) {
-  ExtensionUpdaterTest::TestDetermineUpdatesPending();
+  TestDetermineUpdatesPending();
 }
 
 TEST_F(ExtensionUpdaterTest, TestMultipleManifestDownloading) {
-  ExtensionUpdaterTest::TestMultipleManifestDownloading();
+  TestMultipleManifestDownloading();
 }
 
 TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloading) {
-  ExtensionUpdaterTest::TestSingleExtensionDownloading(false);
+  TestSingleExtensionDownloading(false);
 }
 
 TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) {
-  ExtensionUpdaterTest::TestSingleExtensionDownloading(true);
+  TestSingleExtensionDownloading(true);
 }
 
 TEST_F(ExtensionUpdaterTest, TestBlacklistDownloading) {
-  ExtensionUpdaterTest::TestBlacklistDownloading();
+  TestBlacklistDownloading();
 }
 
 TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesFail) {
-  ExtensionUpdaterTest::TestMultipleExtensionDownloading(false);
+  TestMultipleExtensionDownloading(false);
 }
 TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesSucceed) {
-  ExtensionUpdaterTest::TestMultipleExtensionDownloading(true);
+  TestMultipleExtensionDownloading(true);
 }
 
 TEST_F(ExtensionUpdaterTest, TestGalleryRequestsWithOrganicBrand) {
-  ExtensionUpdaterTest::TestGalleryRequestsWithBrand(true);
+  TestGalleryRequestsWithBrand(true);
 }
 
 TEST_F(ExtensionUpdaterTest, TestGalleryRequestsWithNonOrganicBrand) {
-  ExtensionUpdaterTest::TestGalleryRequestsWithBrand(false);
+  TestGalleryRequestsWithBrand(false);
 }
 
 TEST_F(ExtensionUpdaterTest, TestHandleManifestResults) {
-  ExtensionUpdaterTest::TestHandleManifestResults();
+  TestHandleManifestResults();
+}
+
+TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
+  TestURLFetcherFactory factory;
+  ServiceForManifestTests service;
+  ExtensionUpdater updater(&service, service.extension_prefs(),
+                           service.pref_service(), service.profile(),
+                           kUpdateFrequencySecs);
+  MockExtensionDownloaderDelegate delegate;
+  // Set the downloader directly, so that all its events end up in the mock
+  // |delegate|.
+  ExtensionDownloader* downloader =
+      new ExtensionDownloader(&delegate, service.request_context());
+  ResetDownloader(&updater, downloader);
+
+  // Non-internal non-external extensions should be rejected.
+  ExtensionList extensions;
+  service.CreateTestExtensions(1, 1, &extensions, NULL, Extension::INVALID);
+  service.CreateTestExtensions(2, 1, &extensions, NULL, Extension::INTERNAL);
+  ASSERT_EQ(2u, extensions.size());
+  const std::string& id = extensions[1]->id();
+
+  // These expectations fail if the delegate's methods are invoked for the
+  // first extension, which has a non-matching id.
+  EXPECT_CALL(delegate, GetUpdateUrlData(id)).WillOnce(Return(""));
+  EXPECT_CALL(delegate, GetPingDataForExtension(id, _));
+
+  service.set_extensions(extensions);
+  updater.set_blacklist_checks_enabled(false);
+  updater.Start();
+  updater.CheckNow();
 }
 
 TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
-  MessageLoop message_loop;
-  content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
+  TestURLFetcherFactory factory;
   MockService service;
-  ManifestFetchesBuilder builder(&service, service.extension_prefs());
+  MockExtensionDownloaderDelegate delegate;
+  scoped_ptr<ExtensionDownloader> downloader(
+      new ExtensionDownloader(&delegate, service.request_context()));
+  EXPECT_EQ(0u, ManifestFetchersCount(downloader.get()));
 
-  // Non-internal non-external extensions should be rejected.
-  {
-    ExtensionList extensions;
-    service.CreateTestExtensions(1, 1, &extensions, NULL, Extension::INVALID);
-    ASSERT_FALSE(extensions.empty());
-    builder.AddExtension(*extensions[0]);
-    EXPECT_TRUE(builder.GetFetches().empty());
-  }
+  // First, verify that adding valid extensions does invoke the callbacks on
+  // the delegate.
+  std::string id = GenerateId("foo");
+  EXPECT_CALL(delegate, GetPingDataForExtension(id, _)).WillOnce(Return(false));
+  EXPECT_TRUE(
+      downloader->AddPendingExtension(id, GURL("http://example.com/update")));
+  downloader->StartAllPending();
+  Mock::VerifyAndClearExpectations(&delegate);
+  EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
 
   // Extensions with invalid update URLs should be rejected.
-  builder.AddPendingExtension(
-      GenerateId("foo"),
-      Extension::INTERNAL,
-      GURL("http:google.com:foo"));
-  EXPECT_TRUE(builder.GetFetches().empty());
+  id = GenerateId("foo2");
+  EXPECT_FALSE(
+      downloader->AddPendingExtension(id, GURL("http:google.com:foo")));
+  downloader->StartAllPending();
+  EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
 
   // Extensions with empty IDs should be rejected.
-  builder.AddPendingExtension(
-      "",
-      Extension::INTERNAL,
-      GURL());
-  EXPECT_TRUE(builder.GetFetches().empty());
+  EXPECT_FALSE(downloader->AddPendingExtension("", GURL()));
+  downloader->StartAllPending();
+  EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
 
   // TODO(akalin): Test that extensions with empty update URLs
   // converted from user scripts are rejected.
 
+  // Reset the ExtensionDownloader so that it drops the current fetcher.
+  downloader.reset(
+      new ExtensionDownloader(&delegate, service.request_context()));
+  EXPECT_EQ(0u, ManifestFetchersCount(downloader.get()));
+
   // Extensions with empty update URLs should have a default one
   // filled in.
-  builder.AddPendingExtension(
-      GenerateId("foo"),
-      Extension::INTERNAL,
-      GURL());
+  id = GenerateId("foo3");
+  EXPECT_CALL(delegate, GetPingDataForExtension(id, _)).WillOnce(Return(false));
+  EXPECT_TRUE(downloader->AddPendingExtension(id, GURL()));
+  downloader->StartAllPending();
+  EXPECT_EQ(1u, ManifestFetchersCount(downloader.get()));
 
-  std::vector<ManifestFetchData*> fetches = builder.GetFetches();
-  ASSERT_EQ(1u, fetches.size());
-  scoped_ptr<ManifestFetchData> fetch(fetches[0]);
-  fetches.clear();
-  EXPECT_FALSE(fetch->base_url().is_empty());
-  EXPECT_FALSE(fetch->full_url().is_empty());
+  TestURLFetcher* fetcher =
+      factory.GetFetcherByID(ExtensionDownloader::kManifestFetcherId);
+  ASSERT_TRUE(fetcher);
+  EXPECT_FALSE(fetcher->GetOriginalURL().is_empty());
 }
 
 TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
+  TestURLFetcherFactory factory;
+  MockService service;
+  MockExtensionDownloaderDelegate delegate;
+  ExtensionDownloader downloader(&delegate, service.request_context());
 
-    ServiceForManifestTests service;
-    TestURLFetcherFactory factory;
-    ExtensionUpdater updater(
-        &service, service.extension_prefs(), service.pref_service(),
-        service.profile(), kUpdateFrequencySecs);
-    updater.Start();
-    updater.StartUpdateCheck(new ManifestFetchData(GURL()));
-    // This should delete the newly-created ManifestFetchData.
-    updater.StartUpdateCheck(new ManifestFetchData(GURL()));
-    // This should add into |manifests_pending_|.
-    updater.StartUpdateCheck(new ManifestFetchData(
-        GURL("http://www.google.com")));
-    // This should clear out |manifests_pending_|.
-    updater.Stop();
+  StartUpdateCheck(&downloader, new ManifestFetchData(GURL()));
+  // This should delete the newly-created ManifestFetchData.
+  StartUpdateCheck(&downloader, new ManifestFetchData(GURL()));
+  // This should add into |manifests_pending_|.
+  StartUpdateCheck(&downloader, new ManifestFetchData(GURL(
+      GURL("http://www.google.com"))));
+  // The dtor of |downloader| should delete the pending fetchers.
 }
 
 TEST_F(ExtensionUpdaterTest, TestCheckSoon) {
-    MessageLoop message_loop;
-    content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
-    content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop);
-
-    ServiceForManifestTests service;
-    TestURLFetcherFactory factory;
-    ExtensionUpdater updater(
-        &service, service.extension_prefs(), service.pref_service(),
-        service.profile(), kUpdateFrequencySecs);
-    EXPECT_FALSE(updater.WillCheckSoon());
-    updater.Start();
-    EXPECT_FALSE(updater.WillCheckSoon());
-    updater.CheckSoon();
-    EXPECT_TRUE(updater.WillCheckSoon());
-    updater.CheckSoon();
-    EXPECT_TRUE(updater.WillCheckSoon());
-    ExtensionUpdaterTest::SimulateCheckSoon(updater, &message_loop);
-    EXPECT_FALSE(updater.WillCheckSoon());
-    updater.CheckSoon();
-    EXPECT_TRUE(updater.WillCheckSoon());
-    updater.Stop();
-    EXPECT_FALSE(updater.WillCheckSoon());
+  ServiceForManifestTests service;
+  TestURLFetcherFactory factory;
+  ExtensionUpdater updater(
+      &service, service.extension_prefs(), service.pref_service(),
+      service.profile(), kUpdateFrequencySecs);
+  EXPECT_FALSE(updater.WillCheckSoon());
+  updater.Start();
+  EXPECT_FALSE(updater.WillCheckSoon());
+  updater.CheckSoon();
+  EXPECT_TRUE(updater.WillCheckSoon());
+  updater.CheckSoon();
+  EXPECT_TRUE(updater.WillCheckSoon());
+  RunAllPending();
+  EXPECT_FALSE(updater.WillCheckSoon());
+  updater.CheckSoon();
+  EXPECT_TRUE(updater.WillCheckSoon());
+  updater.Stop();
+  EXPECT_FALSE(updater.WillCheckSoon());
 }
 
 // TODO(asargent) - (http://crbug.com/12780) add tests for:
@@ -1290,3 +1450,5 @@
 // -An extension gets manually updated to v3 while we're downloading v2 (ie
 //  you don't get downgraded accidentally)
 // -An update manifest mentions multiple updates
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/manifest_fetch_data.cc b/chrome/browser/extensions/updater/manifest_fetch_data.cc
new file mode 100644
index 0000000..a5f8dda9
--- /dev/null
+++ b/chrome/browser/extensions/updater/manifest_fetch_data.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "chrome/browser/google/google_util.h"
+#include "net/base/escape.h"
+
+namespace {
+
+// Maximum length of an extension manifest update check url, since it is a GET
+// request. We want to stay under 2K because of proxies, etc.
+const int kExtensionsManifestMaxURLSize = 2000;
+
+}  // namespace
+
+namespace extensions {
+
+ManifestFetchData::ManifestFetchData(const GURL& update_url)
+    : base_url_(update_url),
+      full_url_(update_url) {
+}
+
+ManifestFetchData::~ManifestFetchData() {}
+
+// The format for request parameters in update checks is:
+//
+//   ?x=EXT1_INFO&x=EXT2_INFO
+//
+// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
+//
+//   id=EXTENSION_ID&v=VERSION&uc
+//
+// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
+// looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
+// ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
+// These values will each be present at most once every 24 hours, and indicate
+// the number of days since the last time it was present in an update check.
+//
+// So for two extensions like:
+//   Extension 1- id:aaaa version:1.1
+//   Extension 2- id:bbbb version:2.0
+//
+// the full update url would be:
+//   http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
+//
+// (Note that '=' is %3D and '&' is %26 when urlencoded.)
+bool ManifestFetchData::AddExtension(std::string id, std::string version,
+                                     const PingData* ping_data,
+                                     const std::string& update_url_data,
+                                     const std::string& install_source) {
+  if (extension_ids_.find(id) != extension_ids_.end()) {
+    NOTREACHED() << "Duplicate extension id " << id;
+    return false;
+  }
+
+  // Compute the string we'd append onto the full_url_, and see if it fits.
+  std::vector<std::string> parts;
+  parts.push_back("id=" + id);
+  parts.push_back("v=" + version);
+  if (!install_source.empty())
+    parts.push_back("installsource=" + install_source);
+  parts.push_back("uc");
+
+  if (!update_url_data.empty()) {
+    // Make sure the update_url_data string is escaped before using it so that
+    // there is no chance of overriding the id or v other parameter value
+    // we place into the x= value.
+    parts.push_back("ap=" + net::EscapeQueryParamValue(update_url_data, true));
+  }
+
+  // Append brand code, rollcall and active ping parameters.
+  if (base_url_.DomainIs("google.com")) {
+#if defined(GOOGLE_CHROME_BUILD)
+    std::string brand;
+    google_util::GetBrand(&brand);
+    if (!brand.empty() && !google_util::IsOrganic(brand))
+      parts.push_back("brand=" + brand);
+#endif
+
+    std::string ping_value;
+    pings_[id] = PingData(0, 0);
+
+    if (ping_data) {
+      if (ping_data->rollcall_days == kNeverPinged ||
+          ping_data->rollcall_days > 0) {
+        ping_value += "r=" + base::IntToString(ping_data->rollcall_days);
+        pings_[id].rollcall_days = ping_data->rollcall_days;
+      }
+      if (ping_data->active_days == kNeverPinged ||
+          ping_data->active_days > 0) {
+        if (!ping_value.empty())
+          ping_value += "&";
+        ping_value += "a=" + base::IntToString(ping_data->active_days);
+        pings_[id].active_days = ping_data->active_days;
+      }
+    }
+    if (!ping_value.empty())
+      parts.push_back("ping=" + net::EscapeQueryParamValue(ping_value, true));
+  }
+
+  std::string extra = full_url_.has_query() ? "&" : "?";
+  extra += "x=" + net::EscapeQueryParamValue(JoinString(parts, '&'), true);
+
+  // Check against our max url size, exempting the first extension added.
+  int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
+  if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
+    UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
+    return false;
+  }
+  UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
+
+  // We have room so go ahead and add the extension.
+  extension_ids_.insert(id);
+  full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
+  return true;
+}
+
+bool ManifestFetchData::Includes(const std::string& extension_id) const {
+  return extension_ids_.find(extension_id) != extension_ids_.end();
+}
+
+bool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
+  std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
+  if (i == pings_.end())
+    return false;
+  int value = 0;
+  if (type == ROLLCALL)
+    value = i->second.rollcall_days;
+  else if (type == ACTIVE)
+    value = i->second.active_days;
+  else
+    NOTREACHED();
+  return value == kNeverPinged || value > 0;
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/manifest_fetch_data.h b/chrome/browser/extensions/updater/manifest_fetch_data.h
new file mode 100644
index 0000000..6468c1e
--- /dev/null
+++ b/chrome/browser/extensions/updater/manifest_fetch_data.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+
+namespace extensions {
+
+// To save on server resources we can request updates for multiple extensions
+// in one manifest check. This class helps us keep track of the id's for a
+// given fetch, building up the actual URL, and what if anything to include
+// in the ping parameter.
+class ManifestFetchData {
+ public:
+  static const int kNeverPinged = -1;
+
+  // Each ping type is sent at most once per day.
+  enum PingType {
+    // Used for counting total installs of an extension/app/theme.
+    ROLLCALL,
+
+    // Used for counting number of active users of an app, where "active" means
+    // the app was launched at least once since the last active ping.
+    ACTIVE,
+  };
+
+  struct PingData {
+    // The number of days it's been since our last rollcall or active ping,
+    // respectively. These are calculated based on the start of day from the
+    // server's perspective.
+    int rollcall_days;
+    int active_days;
+
+    PingData() : rollcall_days(0), active_days(0) {}
+    PingData(int rollcall, int active)
+        : rollcall_days(rollcall), active_days(active) {}
+  };
+
+  explicit ManifestFetchData(const GURL& update_url);
+  ~ManifestFetchData();
+
+  // Returns true if this extension information was successfully added. If the
+  // return value is false it means the full_url would have become too long, and
+  // this ManifestFetchData object remains unchanged.
+  bool AddExtension(std::string id, std::string version,
+                    const PingData* ping_data,
+                    const std::string& update_url_data,
+                    const std::string& install_source);
+
+  const GURL& base_url() const { return base_url_; }
+  const GURL& full_url() const { return full_url_; }
+  int extension_count() { return extension_ids_.size(); }
+  const std::set<std::string>& extension_ids() const { return extension_ids_; }
+
+  // Returns true if the given id is included in this manifest fetch.
+  bool Includes(const std::string& extension_id) const;
+
+  // Returns true if a ping parameter for |type| was added to full_url for this
+  // extension id.
+  bool DidPing(std::string extension_id, PingType type) const;
+
+ private:
+  // The set of extension id's for this ManifestFetchData.
+  std::set<std::string> extension_ids_;
+
+  // The set of ping data we actually sent.
+  std::map<std::string, PingData> pings_;
+
+  // The base update url without any arguments added.
+  GURL base_url_;
+
+  // The base update url plus arguments indicating the id, version, etc.
+  // information about each extension.
+  GURL full_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_MANIFEST_FETCH_DATA_H_
diff --git a/chrome/browser/extensions/updater/safe_manifest_parser.cc b/chrome/browser/extensions/updater/safe_manifest_parser.cc
new file mode 100644
index 0000000..c633dafa
--- /dev/null
+++ b/chrome/browser/extensions/updater/safe_manifest_parser.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/updater/safe_manifest_parser.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/utility_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "ipc/ipc_message_macros.h"
+
+using content::BrowserThread;
+
+namespace extensions {
+
+SafeManifestParser::SafeManifestParser(const std::string& xml,
+                                       ManifestFetchData* fetch_data,
+                                       const UpdateCallback& update_callback)
+    : xml_(xml),
+      fetch_data_(fetch_data),
+      update_callback_(update_callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+SafeManifestParser::~SafeManifestParser() {
+  // If we're using UtilityProcessHost, we may not be destroyed on
+  // the UI or IO thread.
+}
+
+void SafeManifestParser::Start() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!BrowserThread::PostTask(
+          BrowserThread::IO, FROM_HERE,
+          base::Bind(&SafeManifestParser::ParseInSandbox, this))) {
+    NOTREACHED();
+  }
+}
+
+void SafeManifestParser::ParseInSandbox() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  // TODO(asargent) we shouldn't need to do this branch here - instead
+  // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
+  bool use_utility_process = content::ResourceDispatcherHost::Get() &&
+      !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
+  if (use_utility_process) {
+    content::UtilityProcessHost* host = content::UtilityProcessHost::Create(
+        this, BrowserThread::UI);
+    host->EnableZygote();
+    host->Send(new ChromeUtilityMsg_ParseUpdateManifest(xml_));
+  } else {
+    UpdateManifest manifest;
+    if (manifest.Parse(xml_)) {
+      if (!BrowserThread::PostTask(
+              BrowserThread::UI, FROM_HERE,
+              base::Bind(
+                  &SafeManifestParser::OnParseUpdateManifestSucceeded, this,
+                  manifest.results()))) {
+        NOTREACHED();
+      }
+    } else {
+      if (!BrowserThread::PostTask(
+              BrowserThread::UI, FROM_HERE,
+              base::Bind(
+                  &SafeManifestParser::OnParseUpdateManifestFailed, this,
+                  manifest.errors()))) {
+        NOTREACHED();
+      }
+    }
+  }
+}
+
+bool SafeManifestParser::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(SafeManifestParser, message)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Succeeded,
+                        OnParseUpdateManifestSucceeded)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseUpdateManifest_Failed,
+                        OnParseUpdateManifestFailed)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void SafeManifestParser::OnParseUpdateManifestSucceeded(
+    const UpdateManifest::Results& results) {
+  VLOG(2) << "parsing manifest succeeded (" << fetch_data_->full_url() << ")";
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  update_callback_.Run(*fetch_data_, &results);
+}
+
+void SafeManifestParser::OnParseUpdateManifestFailed(
+    const std::string& error_message) {
+  VLOG(2) << "parsing manifest failed (" << fetch_data_->full_url() << ")";
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
+  update_callback_.Run(*fetch_data_, NULL);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/safe_manifest_parser.h b/chrome/browser/extensions/updater/safe_manifest_parser.h
new file mode 100644
index 0000000..9440e7eab
--- /dev/null
+++ b/chrome/browser/extensions/updater/safe_manifest_parser.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
+#define CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
+#pragma once
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/updater/manifest_fetch_data.h"
+#include "chrome/common/extensions/update_manifest.h"
+#include "content/public/browser/utility_process_host_client.h"
+
+namespace extensions {
+
+// Utility class to handle doing xml parsing in a sandboxed utility process.
+class SafeManifestParser : public content::UtilityProcessHostClient {
+ public:
+  // Callback that is invoked when the manifest results are ready.
+  typedef base::Callback<void(const ManifestFetchData&,
+                              const UpdateManifest::Results*)> UpdateCallback;
+
+  // Takes ownership of |fetch_data|.
+  SafeManifestParser(const std::string& xml,
+                     ManifestFetchData* fetch_data,
+                     const UpdateCallback& update_callback);
+
+  virtual ~SafeManifestParser();
+
+  // Posts a task over to the IO loop to start the parsing of xml_ in a
+  // utility process.
+  void Start();
+
+ private:
+  // Creates the sandboxed utility process and tells it to start parsing.
+  void ParseInSandbox();
+
+  // UtilityProcessHostClient implementation.
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+  void OnParseUpdateManifestSucceeded(const UpdateManifest::Results& results);
+  void OnParseUpdateManifestFailed(const std::string& error_message);
+
+  const std::string xml_;
+
+  // Should be accessed only on UI thread.
+  scoped_ptr<ManifestFetchData> fetch_data_;
+  UpdateCallback update_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeManifestParser);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_SAFE_MANIFEST_PARSER_H_
diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc
index 0725819..dd8186d 100644
--- a/chrome/browser/first_run/first_run_win.cc
+++ b/chrome/browser/first_run/first_run_win.cc
@@ -22,7 +22,7 @@
 #include "base/win/windows_version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/first_run/first_run_import_observer.h"
 #include "chrome/browser/first_run/first_run_internal.h"
 #include "chrome/browser/importer/importer_host.h"
diff --git a/chrome/browser/metrics/metrics_service.h b/chrome/browser/metrics/metrics_service.h
index 91eb0ead..4761f5d2 100644
--- a/chrome/browser/metrics/metrics_service.h
+++ b/chrome/browser/metrics/metrics_service.h
@@ -32,7 +32,6 @@
 
 class BookmarkModel;
 class BookmarkNode;
-class ManifestFetchesBuilder;
 class MetricsReportingScheduler;
 class PrefService;
 class Profile;
@@ -47,6 +46,10 @@
 class RenderProcessHost;
 }
 
+namespace extensions {
+class ExtensionDownloader;
+}
+
 namespace prerender {
 bool IsOmniboxEnabled(Profile* profile);
 }
@@ -434,7 +437,7 @@
  private:
   friend class InstantFieldTrial;
   friend bool prerender::IsOmniboxEnabled(Profile* profile);
-  friend class ManifestFetchesBuilder;
+  friend class extensions::ExtensionDownloader;
 
   // Returns true if prefs::kMetricsReportingEnabled is set.
   static bool IsMetricsReportingEnabled();
diff --git a/chrome/browser/sync/glue/theme_util.cc b/chrome/browser/sync/glue/theme_util.cc
index 03a1ca1..b60b13e 100644
--- a/chrome/browser/sync/glue/theme_util.cc
+++ b/chrome/browser/sync/glue/theme_util.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.cc b/chrome/browser/ui/webui/options/extension_settings_handler.cc
index 6ea248df..f04b6a5 100644
--- a/chrome/browser/ui/webui/options/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/options/extension_settings_handler.cc
@@ -18,9 +18,9 @@
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_updater.h"
 #include "chrome/browser/extensions/extension_warning_set.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/google/google_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tab_contents/background_contents.h"
@@ -453,7 +453,7 @@
 }
 
 void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) {
-  ExtensionUpdater* updater = extension_service_->updater();
+  extensions::ExtensionUpdater* updater = extension_service_->updater();
   if (updater)
     updater->CheckNow();
 }