Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/extensions/extension_updater.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 
     10 #include "base/compiler_specific.h"
     11 #include "base/logging.h"
     12 #include "base/file_util.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/rand_util.h"
     15 #include "base/stl_util-inl.h"
     16 #include "base/string_number_conversions.h"
     17 #include "base/string_split.h"
     18 #include "base/string_util.h"
     19 #include "base/time.h"
     20 #include "base/threading/thread.h"
     21 #include "base/version.h"
     22 #include "crypto/sha2.h"
     23 #include "content/common/notification_service.h"
     24 #include "chrome/browser/browser_process.h"
     25 #include "chrome/browser/extensions/extension_error_reporter.h"
     26 #include "chrome/browser/extensions/extension_service.h"
     27 #include "chrome/browser/prefs/pref_service.h"
     28 #include "chrome/browser/profiles/profile.h"
     29 #include "chrome/browser/utility_process_host.h"
     30 #include "chrome/common/chrome_switches.h"
     31 #include "chrome/common/chrome_version_info.h"
     32 #include "chrome/common/extensions/extension.h"
     33 #include "chrome/common/extensions/extension_constants.h"
     34 #include "chrome/common/extensions/extension_file_util.h"
     35 #include "chrome/common/pref_names.h"
     36 #include "googleurl/src/gurl.h"
     37 #include "net/base/escape.h"
     38 #include "net/base/load_flags.h"
     39 #include "net/url_request/url_request_status.h"
     40 
     41 #if defined(OS_MACOSX)
     42 #include "base/sys_string_conversions.h"
     43 #endif
     44 
     45 #define SEND_ACTIVE_PINGS 1
     46 
     47 using base::RandDouble;
     48 using base::RandInt;
     49 using base::Time;
     50 using base::TimeDelta;
     51 using prefs::kExtensionBlacklistUpdateVersion;
     52 using prefs::kLastExtensionsUpdateCheck;
     53 using prefs::kNextExtensionsUpdateCheck;
     54 
     55 // Update AppID for extension blacklist.
     56 const char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist";
     57 
     58 // Wait at least 5 minutes after browser startup before we do any checks. If you
     59 // change this value, make sure to update comments where it is used.
     60 const int kStartupWaitSeconds = 60 * 5;
     61 
     62 // For sanity checking on update frequency - enforced in release mode only.
     63 static const int kMinUpdateFrequencySeconds = 30;
     64 static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
     65 
     66 // Maximum length of an extension manifest update check url, since it is a GET
     67 // request. We want to stay under 2K because of proxies, etc.
     68 static const int kExtensionsManifestMaxURLSize = 2000;
     69 
     70 ManifestFetchData::ManifestFetchData(const GURL& update_url)
     71     : base_url_(update_url),
     72       full_url_(update_url) {
     73 }
     74 
     75 ManifestFetchData::~ManifestFetchData() {}
     76 
     77 // The format for request parameters in update checks is:
     78 //
     79 //   ?x=EXT1_INFO&x=EXT2_INFO
     80 //
     81 // where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
     82 //
     83 //   id=EXTENSION_ID&v=VERSION&uc
     84 //
     85 // Additionally, we may include the parameter ping=PING_DATA where PING_DATA
     86 // looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
     87 // ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
     88 // These values will each be present at most once every 24 hours, and indicate
     89 // the number of days since the last time it was present in an update check.
     90 //
     91 // So for two extensions like:
     92 //   Extension 1- id:aaaa version:1.1
     93 //   Extension 2- id:bbbb version:2.0
     94 //
     95 // the full update url would be:
     96 //   http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
     97 //
     98 // (Note that '=' is %3D and '&' is %26 when urlencoded.)
     99 bool ManifestFetchData::AddExtension(std::string id, std::string version,
    100                                      const PingData& ping_data,
    101                                      const std::string& update_url_data) {
    102   if (extension_ids_.find(id) != extension_ids_.end()) {
    103     NOTREACHED() << "Duplicate extension id " << id;
    104     return false;
    105   }
    106 
    107   // Compute the string we'd append onto the full_url_, and see if it fits.
    108   std::vector<std::string> parts;
    109   parts.push_back("id=" + id);
    110   parts.push_back("v=" + version);
    111   parts.push_back("uc");
    112 
    113   if (!update_url_data.empty()) {
    114     // Make sure the update_url_data string is escaped before using it so that
    115     // there is no chance of overriding the id or v other parameter value
    116     // we place into the x= value.
    117     parts.push_back("ap=" + EscapeQueryParamValue(update_url_data, true));
    118   }
    119 
    120   // Append rollcall and active ping parameters.
    121   if (base_url_.DomainIs("google.com")) {
    122     std::string ping_value;
    123     pings_[id] = PingData(0, 0);
    124 
    125     if (ping_data.rollcall_days == kNeverPinged ||
    126         ping_data.rollcall_days > 0) {
    127       ping_value += "r=" + base::IntToString(ping_data.rollcall_days);
    128       pings_[id].rollcall_days = ping_data.rollcall_days;
    129     }
    130 #if SEND_ACTIVE_PINGS
    131     if (ping_data.active_days == kNeverPinged || ping_data.active_days > 0) {
    132       if (!ping_value.empty())
    133         ping_value += "&";
    134       ping_value += "a=" + base::IntToString(ping_data.active_days);
    135       pings_[id].active_days = ping_data.active_days;
    136     }
    137 #endif  // SEND_ACTIVE_PINGS
    138     if (!ping_value.empty())
    139       parts.push_back("ping=" + EscapeQueryParamValue(ping_value, true));
    140   }
    141 
    142   std::string extra = full_url_.has_query() ? "&" : "?";
    143   extra += "x=" + EscapeQueryParamValue(JoinString(parts, '&'), true);
    144 
    145   // Check against our max url size, exempting the first extension added.
    146   int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
    147   if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
    148     UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
    149     return false;
    150   }
    151   UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
    152 
    153   // We have room so go ahead and add the extension.
    154   extension_ids_.insert(id);
    155   full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
    156   return true;
    157 }
    158 
    159 bool ManifestFetchData::Includes(const std::string& extension_id) const {
    160   return extension_ids_.find(extension_id) != extension_ids_.end();
    161 }
    162 
    163 bool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
    164   std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
    165   if (i == pings_.end())
    166     return false;
    167   int value = 0;
    168   if (type == ROLLCALL)
    169     value = i->second.rollcall_days;
    170   else if (type == ACTIVE)
    171     value = i->second.active_days;
    172   else
    173     NOTREACHED();
    174   return value == kNeverPinged || value > 0;
    175 }
    176 
    177 namespace {
    178 
    179 // When we've computed a days value, we want to make sure we don't send a
    180 // negative value (due to the system clock being set backwards, etc.), since -1
    181 // is a special sentinel value that means "never pinged", and other negative
    182 // values don't make sense.
    183 static int SanitizeDays(int days) {
    184   if (days < 0)
    185     return 0;
    186   return days;
    187 }
    188 
    189 // Calculates the value to use for the ping days parameter.
    190 static int CalculatePingDays(const Time& last_ping_day) {
    191   int days = ManifestFetchData::kNeverPinged;
    192   if (!last_ping_day.is_null()) {
    193     days = SanitizeDays((Time::Now() - last_ping_day).InDays());
    194   }
    195   return days;
    196 }
    197 
    198 static int CalculateActivePingDays(const Time& last_active_ping_day,
    199                                    bool hasActiveBit) {
    200   if (!hasActiveBit)
    201     return 0;
    202   if (last_active_ping_day.is_null())
    203     return ManifestFetchData::kNeverPinged;
    204   return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
    205 }
    206 
    207 }  // namespace
    208 
    209 ManifestFetchesBuilder::ManifestFetchesBuilder(
    210     ExtensionServiceInterface* service,
    211     ExtensionPrefs* prefs)
    212     : service_(service), prefs_(prefs) {
    213   DCHECK(service_);
    214   DCHECK(prefs_);
    215 }
    216 
    217 ManifestFetchesBuilder::~ManifestFetchesBuilder() {}
    218 
    219 void ManifestFetchesBuilder::AddExtension(const Extension& extension) {
    220   // Skip extensions with empty update URLs converted from user
    221   // scripts.
    222   if (extension.converted_from_user_script() &&
    223       extension.update_url().is_empty()) {
    224     return;
    225   }
    226 
    227   // If the extension updates itself from the gallery, ignore any update URL
    228   // data.  At the moment there is no extra data that an extension can
    229   // communicate to the the gallery update servers.
    230   std::string update_url_data;
    231   if (!extension.UpdatesFromGallery())
    232     update_url_data = prefs_->GetUpdateUrlData(extension.id());
    233 
    234   AddExtensionData(extension.location(),
    235                    extension.id(),
    236                    *extension.version(),
    237                    extension.GetType(),
    238                    extension.update_url(), update_url_data);
    239 }
    240 
    241 void ManifestFetchesBuilder::AddPendingExtension(
    242     const std::string& id,
    243     const PendingExtensionInfo& info) {
    244   // Use a zero version to ensure that a pending extension will always
    245   // be updated, and thus installed (assuming all extensions have
    246   // non-zero versions).
    247   scoped_ptr<Version> version(
    248       Version::GetVersionFromString("0.0.0.0"));
    249 
    250   AddExtensionData(
    251       info.install_source(), id, *version,
    252       Extension::TYPE_UNKNOWN, info.update_url(), "");
    253 }
    254 
    255 void ManifestFetchesBuilder::ReportStats() const {
    256   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
    257                            url_stats_.extension_count);
    258   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
    259                            url_stats_.theme_count);
    260   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
    261                            url_stats_.app_count);
    262   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
    263                            url_stats_.pending_count);
    264   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
    265                            url_stats_.google_url_count);
    266   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
    267                            url_stats_.other_url_count);
    268   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
    269                            url_stats_.no_url_count);
    270 }
    271 
    272 std::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() {
    273   std::vector<ManifestFetchData*> fetches;
    274   fetches.reserve(fetches_.size());
    275   for (std::multimap<GURL, ManifestFetchData*>::iterator it =
    276            fetches_.begin(); it != fetches_.end(); ++it) {
    277     fetches.push_back(it->second);
    278   }
    279   fetches_.clear();
    280   url_stats_ = URLStats();
    281   return fetches;
    282 }
    283 
    284 void ManifestFetchesBuilder::AddExtensionData(
    285     Extension::Location location,
    286     const std::string& id,
    287     const Version& version,
    288     Extension::Type extension_type,
    289     GURL update_url,
    290     const std::string& update_url_data) {
    291   if (!Extension::IsAutoUpdateableLocation(location)) {
    292     return;
    293   }
    294 
    295   // Skip extensions with non-empty invalid update URLs.
    296   if (!update_url.is_empty() && !update_url.is_valid()) {
    297     LOG(WARNING) << "Extension " << id << " has invalid update url "
    298                  << update_url;
    299     return;
    300   }
    301 
    302   // Skip extensions with empty IDs.
    303   if (id.empty()) {
    304     LOG(WARNING) << "Found extension with empty ID";
    305     return;
    306   }
    307 
    308   if (update_url.DomainIs("google.com")) {
    309     url_stats_.google_url_count++;
    310   } else if (update_url.is_empty()) {
    311     url_stats_.no_url_count++;
    312     // Fill in default update URL.
    313     //
    314     // TODO(akalin): Figure out if we should use the HTTPS version.
    315     update_url = Extension::GalleryUpdateUrl(false);
    316   } else {
    317     url_stats_.other_url_count++;
    318   }
    319 
    320   switch (extension_type) {
    321     case Extension::TYPE_THEME:
    322       ++url_stats_.theme_count;
    323       break;
    324     case Extension::TYPE_EXTENSION:
    325     case Extension::TYPE_USER_SCRIPT:
    326       ++url_stats_.extension_count;
    327       break;
    328     case Extension::TYPE_HOSTED_APP:
    329     case Extension::TYPE_PACKAGED_APP:
    330       ++url_stats_.app_count;
    331       break;
    332     case Extension::TYPE_UNKNOWN:
    333     default:
    334       ++url_stats_.pending_count;
    335       break;
    336   }
    337 
    338   DCHECK(!update_url.is_empty());
    339   DCHECK(update_url.is_valid());
    340 
    341   ManifestFetchData* fetch = NULL;
    342   std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
    343       fetches_.find(update_url);
    344 
    345   // Find or create a ManifestFetchData to add this extension to.
    346   ManifestFetchData::PingData ping_data;
    347   ping_data.rollcall_days = CalculatePingDays(prefs_->LastPingDay(id));
    348   ping_data.active_days =
    349       CalculateActivePingDays(prefs_->LastActivePingDay(id),
    350                               prefs_->GetActiveBit(id));
    351   while (existing_iter != fetches_.end()) {
    352     if (existing_iter->second->AddExtension(id, version.GetString(),
    353                                             ping_data, update_url_data)) {
    354       fetch = existing_iter->second;
    355       break;
    356     }
    357     existing_iter++;
    358   }
    359   if (!fetch) {
    360     fetch = new ManifestFetchData(update_url);
    361     fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch));
    362     bool added = fetch->AddExtension(id, version.GetString(), ping_data,
    363                                      update_url_data);
    364     DCHECK(added);
    365   }
    366 }
    367 
    368 // A utility class to do file handling on the file I/O thread.
    369 class ExtensionUpdaterFileHandler
    370     : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
    371  public:
    372   explicit ExtensionUpdaterFileHandler(
    373       base::WeakPtr<ExtensionUpdater> updater)
    374       : updater_(updater) {
    375     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    376   }
    377 
    378   // Writes crx file data into a tempfile, and calls back the updater.
    379   void WriteTempFile(const std::string& extension_id, const std::string& data,
    380                      const GURL& download_url) {
    381     // Make sure we're running in the right thread.
    382     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    383 
    384     bool failed = false;
    385     FilePath path;
    386     if (!file_util::CreateTemporaryFile(&path)) {
    387       LOG(WARNING) << "Failed to create temporary file path";
    388       failed = true;
    389     } else if (file_util::WriteFile(path, data.c_str(), data.length()) !=
    390         static_cast<int>(data.length())) {
    391       // TODO(asargent) - It would be nice to back off updating altogether if
    392       // the disk is full. (http://crbug.com/12763).
    393       LOG(ERROR) << "Failed to write temporary file";
    394       file_util::Delete(path, false);
    395       failed = true;
    396     }
    397 
    398     if (failed) {
    399       if (!BrowserThread::PostTask(
    400               BrowserThread::UI, FROM_HERE,
    401               NewRunnableMethod(
    402                   this, &ExtensionUpdaterFileHandler::OnCRXFileWriteError,
    403                   extension_id))) {
    404         NOTREACHED();
    405       }
    406     } else {
    407       if (!BrowserThread::PostTask(
    408               BrowserThread::UI, FROM_HERE,
    409               NewRunnableMethod(
    410                   this, &ExtensionUpdaterFileHandler::OnCRXFileWritten,
    411                   extension_id, path, download_url))) {
    412         NOTREACHED();
    413         // Delete |path| since we couldn't post.
    414         extension_file_util::DeleteFile(path, false);
    415       }
    416     }
    417   }
    418 
    419  private:
    420   friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>;
    421 
    422   ~ExtensionUpdaterFileHandler() {
    423     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
    424            BrowserThread::CurrentlyOn(BrowserThread::FILE));
    425   }
    426 
    427   void OnCRXFileWritten(const std::string& id,
    428                         const FilePath& path,
    429                         const GURL& download_url) {
    430     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    431     if (!updater_) {
    432       // Delete |path| since we don't have an updater anymore.
    433       if (!BrowserThread::PostTask(
    434               BrowserThread::FILE, FROM_HERE,
    435               NewRunnableFunction(
    436                   extension_file_util::DeleteFile, path, false))) {
    437         NOTREACHED();
    438       }
    439       return;
    440     }
    441     // The ExtensionUpdater now owns the temp file.
    442     updater_->OnCRXFileWritten(id, path, download_url);
    443   }
    444 
    445   void OnCRXFileWriteError(const std::string& id) {
    446     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    447     if (!updater_) {
    448       return;
    449     }
    450     updater_->OnCRXFileWriteError(id);
    451   }
    452 
    453   // Should be accessed only on UI thread.
    454   base::WeakPtr<ExtensionUpdater> updater_;
    455 };
    456 
    457 ExtensionUpdater::ExtensionFetch::ExtensionFetch()
    458     : id(""),
    459       url(),
    460       package_hash(""),
    461       version("") {}
    462 
    463 ExtensionUpdater::ExtensionFetch::ExtensionFetch(const std::string& i,
    464                                                  const GURL& u,
    465                                                  const std::string& h,
    466                                                  const std::string& v)
    467     : id(i), url(u), package_hash(h), version(v) {}
    468 
    469 ExtensionUpdater::ExtensionFetch::~ExtensionFetch() {}
    470 
    471 ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
    472                                    ExtensionPrefs* extension_prefs,
    473                                    PrefService* prefs,
    474                                    Profile* profile,
    475                                    int frequency_seconds)
    476     : alive_(false),
    477       weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
    478       service_(service), frequency_seconds_(frequency_seconds),
    479       method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
    480       will_check_soon_(false), extension_prefs_(extension_prefs),
    481       prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true) {
    482   Init();
    483 }
    484 
    485 void ExtensionUpdater::Init() {
    486   DCHECK_GE(frequency_seconds_, 5);
    487   DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds);
    488 #ifdef NDEBUG
    489   // In Release mode we enforce that update checks don't happen too often.
    490   frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
    491 #endif
    492   frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
    493 }
    494 
    495 ExtensionUpdater::~ExtensionUpdater() {
    496   Stop();
    497 }
    498 
    499 static void EnsureInt64PrefRegistered(PrefService* prefs,
    500                                       const char name[]) {
    501   if (!prefs->FindPreference(name))
    502     prefs->RegisterInt64Pref(name, 0);
    503 }
    504 
    505 static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
    506   if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion))
    507     prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion, "0");
    508 }
    509 
    510 // The overall goal here is to balance keeping clients up to date while
    511 // avoiding a thundering herd against update servers.
    512 TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
    513   DCHECK(alive_);
    514   // If someone's testing with a quick frequency, just allow it.
    515   if (frequency_seconds_ < kStartupWaitSeconds)
    516     return TimeDelta::FromSeconds(frequency_seconds_);
    517 
    518   // If we've never scheduled a check before, start at frequency_seconds_.
    519   if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
    520     return TimeDelta::FromSeconds(frequency_seconds_);
    521 
    522   // If it's been a long time since our last actual check, we want to do one
    523   // relatively soon.
    524   Time now = Time::Now();
    525   Time last = Time::FromInternalValue(prefs_->GetInt64(
    526       kLastExtensionsUpdateCheck));
    527   int days = (now - last).InDays();
    528   if (days >= 30) {
    529     // Wait 5-10 minutes.
    530     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
    531                                           kStartupWaitSeconds * 2));
    532   } else if (days >= 14) {
    533     // Wait 10-20 minutes.
    534     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
    535                                           kStartupWaitSeconds * 4));
    536   } else if (days >= 3) {
    537     // Wait 20-40 minutes.
    538     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
    539                                           kStartupWaitSeconds * 8));
    540   }
    541 
    542   // Read the persisted next check time, and use that if it isn't too soon.
    543   // Otherwise pick something random.
    544   Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
    545       kNextExtensionsUpdateCheck));
    546   Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
    547   if (saved_next >= earliest) {
    548     return saved_next - now;
    549   } else {
    550     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
    551                                           frequency_seconds_));
    552   }
    553 }
    554 
    555 void ExtensionUpdater::Start() {
    556   DCHECK(!alive_);
    557   // If these are NULL, then that means we've been called after Stop()
    558   // has been called.
    559   DCHECK(service_);
    560   DCHECK(extension_prefs_);
    561   DCHECK(prefs_);
    562   DCHECK(profile_);
    563   DCHECK(!weak_ptr_factory_.HasWeakPtrs());
    564   file_handler_ =
    565       new ExtensionUpdaterFileHandler(weak_ptr_factory_.GetWeakPtr());
    566   alive_ = true;
    567   // Make sure our prefs are registered, then schedule the first check.
    568   EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
    569   EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
    570   EnsureBlacklistVersionPrefRegistered(prefs_);
    571   ScheduleNextCheck(DetermineFirstCheckDelay());
    572 }
    573 
    574 void ExtensionUpdater::Stop() {
    575   weak_ptr_factory_.InvalidateWeakPtrs();
    576   alive_ = false;
    577   file_handler_ = NULL;
    578   service_ = NULL;
    579   extension_prefs_ = NULL;
    580   prefs_ = NULL;
    581   profile_ = NULL;
    582   timer_.Stop();
    583   will_check_soon_ = false;
    584   method_factory_.RevokeAll();
    585   manifest_fetcher_.reset();
    586   extension_fetcher_.reset();
    587   STLDeleteElements(&manifests_pending_);
    588   manifests_pending_.clear();
    589   extensions_pending_.clear();
    590 }
    591 
    592 void ExtensionUpdater::OnURLFetchComplete(
    593     const URLFetcher* source,
    594     const GURL& url,
    595     const net::URLRequestStatus& status,
    596     int response_code,
    597     const ResponseCookies& cookies,
    598     const std::string& data) {
    599   // Stop() destroys all our URLFetchers, which means we shouldn't be
    600   // called after Stop() is called.
    601   DCHECK(alive_);
    602 
    603   if (source == manifest_fetcher_.get()) {
    604     OnManifestFetchComplete(url, status, response_code, data);
    605   } else if (source == extension_fetcher_.get()) {
    606     OnCRXFetchComplete(url, status, response_code, data);
    607   } else {
    608     NOTREACHED();
    609   }
    610   NotifyIfFinished();
    611 }
    612 
    613 // Utility class to handle doing xml parsing in a sandboxed utility process.
    614 class SafeManifestParser : public UtilityProcessHost::Client {
    615  public:
    616   // Takes ownership of |fetch_data|.
    617   SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
    618                      base::WeakPtr<ExtensionUpdater> updater)
    619       : xml_(xml), updater_(updater) {
    620     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    621     fetch_data_.reset(fetch_data);
    622   }
    623 
    624   // Posts a task over to the IO loop to start the parsing of xml_ in a
    625   // utility process.
    626   void Start() {
    627     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    628     if (!BrowserThread::PostTask(
    629             BrowserThread::IO, FROM_HERE,
    630             NewRunnableMethod(
    631                 this, &SafeManifestParser::ParseInSandbox,
    632                 g_browser_process->resource_dispatcher_host()))) {
    633       NOTREACHED();
    634     }
    635   }
    636 
    637   // Creates the sandboxed utility process and tells it to start parsing.
    638   void ParseInSandbox(ResourceDispatcherHost* rdh) {
    639     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    640 
    641     // TODO(asargent) we shouldn't need to do this branch here - instead
    642     // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
    643     bool use_utility_process = rdh &&
    644         !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
    645     if (use_utility_process) {
    646       UtilityProcessHost* host = new UtilityProcessHost(
    647           this, BrowserThread::UI);
    648       host->StartUpdateManifestParse(xml_);
    649     } else {
    650       UpdateManifest manifest;
    651       if (manifest.Parse(xml_)) {
    652         if (!BrowserThread::PostTask(
    653                 BrowserThread::UI, FROM_HERE,
    654                 NewRunnableMethod(
    655                     this, &SafeManifestParser::OnParseUpdateManifestSucceeded,
    656                     manifest.results()))) {
    657           NOTREACHED();
    658         }
    659       } else {
    660         if (!BrowserThread::PostTask(
    661                 BrowserThread::UI, FROM_HERE,
    662                 NewRunnableMethod(
    663                     this, &SafeManifestParser::OnParseUpdateManifestFailed,
    664                     manifest.errors()))) {
    665           NOTREACHED();
    666         }
    667       }
    668     }
    669   }
    670 
    671   // Callback from the utility process when parsing succeeded.
    672   virtual void OnParseUpdateManifestSucceeded(
    673       const UpdateManifest::Results& results) {
    674     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    675     if (!updater_) {
    676       return;
    677     }
    678     updater_->HandleManifestResults(*fetch_data_, &results);
    679   }
    680 
    681   // Callback from the utility process when parsing failed.
    682   virtual void OnParseUpdateManifestFailed(const std::string& error_message) {
    683     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    684     if (!updater_) {
    685       return;
    686     }
    687     LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
    688     updater_->HandleManifestResults(*fetch_data_, NULL);
    689   }
    690 
    691  private:
    692   ~SafeManifestParser() {
    693     // If we're using UtilityProcessHost, we may not be destroyed on
    694     // the UI or IO thread.
    695   }
    696 
    697   const std::string xml_;
    698 
    699   // Should be accessed only on UI thread.
    700   scoped_ptr<ManifestFetchData> fetch_data_;
    701   base::WeakPtr<ExtensionUpdater> updater_;
    702 };
    703 
    704 
    705 void ExtensionUpdater::OnManifestFetchComplete(
    706     const GURL& url,
    707     const net::URLRequestStatus& status,
    708     int response_code,
    709     const std::string& data) {
    710   // We want to try parsing the manifest, and if it indicates updates are
    711   // available, we want to fire off requests to fetch those updates.
    712   if (status.status() == net::URLRequestStatus::SUCCESS &&
    713       (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
    714     scoped_refptr<SafeManifestParser> safe_parser(
    715         new SafeManifestParser(data, current_manifest_fetch_.release(),
    716                                weak_ptr_factory_.GetWeakPtr()));
    717     safe_parser->Start();
    718   } else {
    719     // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
    720     VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
    721             << "' response code:" << response_code;
    722     RemoveFromInProgress(current_manifest_fetch_->extension_ids());
    723   }
    724   manifest_fetcher_.reset();
    725   current_manifest_fetch_.reset();
    726 
    727   // If we have any pending manifest requests, fire off the next one.
    728   if (!manifests_pending_.empty()) {
    729     ManifestFetchData* manifest_fetch = manifests_pending_.front();
    730     manifests_pending_.pop_front();
    731     StartUpdateCheck(manifest_fetch);
    732   }
    733 }
    734 
    735 void ExtensionUpdater::HandleManifestResults(
    736     const ManifestFetchData& fetch_data,
    737     const UpdateManifest::Results* results) {
    738   DCHECK(alive_);
    739 
    740   // Remove all the ids's from in_progress_ids_ (we will add them back in
    741   // below if they actually have updates we need to fetch and install).
    742   RemoveFromInProgress(fetch_data.extension_ids());
    743 
    744   if (!results) {
    745     NotifyIfFinished();
    746     return;
    747   }
    748 
    749   // Examine the parsed manifest and kick off fetches of any new crx files.
    750   std::vector<int> updates = DetermineUpdates(fetch_data, *results);
    751   for (size_t i = 0; i < updates.size(); i++) {
    752     const UpdateManifest::Result* update = &(results->list.at(updates[i]));
    753     const std::string& id = update->extension_id;
    754     in_progress_ids_.insert(id);
    755     if (id != std::string(kBlacklistAppID))
    756       NotifyUpdateFound(update->extension_id);
    757     FetchUpdatedExtension(update->extension_id, update->crx_url,
    758         update->package_hash, update->version);
    759   }
    760 
    761   // If the manifest response included a <daystart> element, we want to save
    762   // that value for any extensions which had sent a ping in the request.
    763   if (fetch_data.base_url().DomainIs("google.com") &&
    764       results->daystart_elapsed_seconds >= 0) {
    765     Time daystart =
    766       Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
    767 
    768     const std::set<std::string>& extension_ids = fetch_data.extension_ids();
    769     std::set<std::string>::const_iterator i;
    770     for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
    771       if (fetch_data.DidPing(*i, ManifestFetchData::ROLLCALL)) {
    772         if (*i == kBlacklistAppID) {
    773           extension_prefs_->SetBlacklistLastPingDay(daystart);
    774         } else if (service_->GetExtensionById(*i, true) != NULL) {
    775           extension_prefs_->SetLastPingDay(*i, daystart);
    776         }
    777       }
    778       if (extension_prefs_->GetActiveBit(*i)) {
    779         extension_prefs_->SetActiveBit(*i, false);
    780         extension_prefs_->SetLastActivePingDay(*i, daystart);
    781       }
    782     }
    783   }
    784   NotifyIfFinished();
    785 }
    786 
    787 void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
    788   DCHECK(alive_);
    789   // Verify sha256 hash value.
    790   char sha256_hash_value[crypto::SHA256_LENGTH];
    791   crypto::SHA256HashString(data, sha256_hash_value, crypto::SHA256_LENGTH);
    792   std::string hash_in_hex = base::HexEncode(sha256_hash_value,
    793                                             crypto::SHA256_LENGTH);
    794 
    795   if (current_extension_fetch_.package_hash != hash_in_hex) {
    796     NOTREACHED() << "Fetched blacklist checksum is not as expected. "
    797       << "Expected: " << current_extension_fetch_.package_hash
    798       << " Actual: " << hash_in_hex;
    799     return;
    800   }
    801   std::vector<std::string> blacklist;
    802   base::SplitString(data, '\n', &blacklist);
    803 
    804   // Tell ExtensionService to update prefs.
    805   service_->UpdateExtensionBlacklist(blacklist);
    806 
    807   // Update the pref value for blacklist version
    808   prefs_->SetString(kExtensionBlacklistUpdateVersion,
    809                     current_extension_fetch_.version);
    810   prefs_->ScheduleSavePersistentPrefs();
    811 }
    812 
    813 void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
    814                                           const net::URLRequestStatus& status,
    815                                           int response_code,
    816                                           const std::string& data) {
    817   if (status.status() == net::URLRequestStatus::SUCCESS &&
    818       (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
    819     if (current_extension_fetch_.id == kBlacklistAppID) {
    820       ProcessBlacklist(data);
    821       in_progress_ids_.erase(current_extension_fetch_.id);
    822     } else {
    823       // Successfully fetched - now write crx to a file so we can have the
    824       // ExtensionService install it.
    825       if (!BrowserThread::PostTask(
    826               BrowserThread::FILE, FROM_HERE,
    827               NewRunnableMethod(
    828                   file_handler_.get(),
    829                   &ExtensionUpdaterFileHandler::WriteTempFile,
    830                   current_extension_fetch_.id, data, url))) {
    831         NOTREACHED();
    832       }
    833     }
    834   } else {
    835     // TODO(asargent) do things like exponential backoff, handling
    836     // 503 Service Unavailable / Retry-After headers, etc. here.
    837     // (http://crbug.com/12546).
    838     VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
    839             << "' response code:" << response_code;
    840   }
    841   extension_fetcher_.reset();
    842   current_extension_fetch_ = ExtensionFetch();
    843 
    844   // If there are any pending downloads left, start one.
    845   if (!extensions_pending_.empty()) {
    846     ExtensionFetch next = extensions_pending_.front();
    847     extensions_pending_.pop_front();
    848     FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
    849   }
    850 }
    851 
    852 void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
    853                                         const FilePath& path,
    854                                         const GURL& download_url) {
    855   DCHECK(alive_);
    856   // The ExtensionService is now responsible for cleaning up the temp file
    857   // at |path|.
    858   service_->UpdateExtension(id, path, download_url);
    859   in_progress_ids_.erase(id);
    860   NotifyIfFinished();
    861 }
    862 
    863 void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
    864   DCHECK(alive_);
    865   in_progress_ids_.erase(id);
    866   NotifyIfFinished();
    867 }
    868 
    869 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
    870   DCHECK(alive_);
    871   DCHECK(!timer_.IsRunning());
    872   DCHECK(target_delay >= TimeDelta::FromSeconds(1));
    873 
    874   // Add +/- 10% random jitter.
    875   double delay_ms = target_delay.InMillisecondsF();
    876   double jitter_factor = (RandDouble() * .2) - 0.1;
    877   delay_ms += delay_ms * jitter_factor;
    878   TimeDelta actual_delay = TimeDelta::FromMilliseconds(
    879       static_cast<int64>(delay_ms));
    880 
    881   // Save the time of next check.
    882   Time next = Time::Now() + actual_delay;
    883   prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
    884   prefs_->ScheduleSavePersistentPrefs();
    885 
    886   timer_.Start(actual_delay, this, &ExtensionUpdater::TimerFired);
    887 }
    888 
    889 void ExtensionUpdater::TimerFired() {
    890   DCHECK(alive_);
    891   CheckNow();
    892 
    893   // If the user has overridden the update frequency, don't bother reporting
    894   // this.
    895   if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
    896     Time last = Time::FromInternalValue(prefs_->GetInt64(
    897         kLastExtensionsUpdateCheck));
    898     if (last.ToInternalValue() != 0) {
    899       // Use counts rather than time so we can use minutes rather than millis.
    900       UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
    901           (Time::Now() - last).InMinutes(),
    902           base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
    903           base::TimeDelta::FromDays(40).InMinutes(),
    904           50);  // 50 buckets seems to be the default.
    905     }
    906   }
    907 
    908   // Save the last check time, and schedule the next check.
    909   int64 now = Time::Now().ToInternalValue();
    910   prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
    911   ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
    912 }
    913 
    914 void ExtensionUpdater::CheckSoon() {
    915   DCHECK(alive_);
    916   if (will_check_soon_) {
    917     return;
    918   }
    919   if (BrowserThread::PostTask(
    920           BrowserThread::UI, FROM_HERE,
    921           method_factory_.NewRunnableMethod(
    922               &ExtensionUpdater::DoCheckSoon))) {
    923     will_check_soon_ = true;
    924   } else {
    925     NOTREACHED();
    926   }
    927 }
    928 
    929 bool ExtensionUpdater::WillCheckSoon() const {
    930   return will_check_soon_;
    931 }
    932 
    933 void ExtensionUpdater::DoCheckSoon() {
    934   DCHECK(will_check_soon_);
    935   CheckNow();
    936   will_check_soon_ = false;
    937 }
    938 
    939 void ExtensionUpdater::CheckNow() {
    940   DCHECK(alive_);
    941   NotifyStarted();
    942   ManifestFetchesBuilder fetches_builder(service_, extension_prefs_);
    943 
    944   const ExtensionList* extensions = service_->extensions();
    945   for (ExtensionList::const_iterator iter = extensions->begin();
    946        iter != extensions->end(); ++iter) {
    947     fetches_builder.AddExtension(**iter);
    948   }
    949 
    950   const PendingExtensionManager* pending_extension_manager =
    951       service_->pending_extension_manager();
    952 
    953   PendingExtensionManager::const_iterator iter;
    954   for (iter = pending_extension_manager->begin();
    955        iter != pending_extension_manager->end(); iter++) {
    956     // TODO(skerner): Move the determination of what gets fetched into
    957     // class PendingExtensionManager.
    958     Extension::Location location = iter->second.install_source();
    959     if (location != Extension::EXTERNAL_PREF &&
    960         location != Extension::EXTERNAL_REGISTRY)
    961       fetches_builder.AddPendingExtension(iter->first, iter->second);
    962   }
    963 
    964   fetches_builder.ReportStats();
    965 
    966   std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches());
    967 
    968   // Start a fetch of the blacklist if needed.
    969   if (blacklist_checks_enabled_) {
    970     // Note: it is very important that we use  the https version of the update
    971     // url here to avoid DNS hijacking of the blacklist, which is not validated
    972     // by a public key signature like .crx files are.
    973     ManifestFetchData* blacklist_fetch =
    974         new ManifestFetchData(Extension::GalleryUpdateUrl(true));
    975     std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
    976     ManifestFetchData::PingData ping_data;
    977     ping_data.rollcall_days =
    978         CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
    979     blacklist_fetch->AddExtension(kBlacklistAppID, version, ping_data, "");
    980     StartUpdateCheck(blacklist_fetch);
    981   }
    982 
    983   // Now start fetching regular extension updates
    984   for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin();
    985        it != fetches.end(); ++it) {
    986     // StartUpdateCheck makes sure the url isn't already downloading or
    987     // scheduled, so we don't need to check before calling it. Ownership of
    988     // fetch is transferred here.
    989     StartUpdateCheck(*it);
    990   }
    991   // We don't want to use fetches after this since StartUpdateCheck()
    992   // takes ownership of its argument.
    993   fetches.clear();
    994 
    995   NotifyIfFinished();
    996 }
    997 
    998 bool ExtensionUpdater::GetExistingVersion(const std::string& id,
    999                                           std::string* version) {
   1000   DCHECK(alive_);
   1001   if (id == kBlacklistAppID) {
   1002     *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
   1003     return true;
   1004   }
   1005   const Extension* extension = service_->GetExtensionById(id, false);
   1006   if (!extension) {
   1007     return false;
   1008   }
   1009   *version = extension->version()->GetString();
   1010   return true;
   1011 }
   1012 
   1013 std::vector<int> ExtensionUpdater::DetermineUpdates(
   1014     const ManifestFetchData& fetch_data,
   1015     const UpdateManifest::Results& possible_updates) {
   1016   DCHECK(alive_);
   1017   std::vector<int> result;
   1018 
   1019   // This will only get set if one of possible_updates specifies
   1020   // browser_min_version.
   1021   scoped_ptr<Version> browser_version;
   1022   PendingExtensionManager* pending_extension_manager =
   1023       service_->pending_extension_manager();
   1024 
   1025   for (size_t i = 0; i < possible_updates.list.size(); i++) {
   1026     const UpdateManifest::Result* update = &possible_updates.list[i];
   1027 
   1028     if (!fetch_data.Includes(update->extension_id))
   1029       continue;
   1030 
   1031     if (!pending_extension_manager->IsIdPending(update->extension_id)) {
   1032       // If we're not installing pending extension, and the update
   1033       // version is the same or older than what's already installed,
   1034       // we don't want it.
   1035       std::string version;
   1036       if (!GetExistingVersion(update->extension_id, &version))
   1037         continue;
   1038 
   1039       scoped_ptr<Version> existing_version(
   1040           Version::GetVersionFromString(version));
   1041       scoped_ptr<Version> update_version(
   1042           Version::GetVersionFromString(update->version));
   1043 
   1044       if (!update_version.get() ||
   1045           update_version->CompareTo(*(existing_version.get())) <= 0) {
   1046         continue;
   1047       }
   1048     }
   1049 
   1050     // If the update specifies a browser minimum version, do we qualify?
   1051     if (update->browser_min_version.length() > 0) {
   1052       // First determine the browser version if we haven't already.
   1053       if (!browser_version.get()) {
   1054         chrome::VersionInfo version_info;
   1055         if (version_info.is_valid()) {
   1056           browser_version.reset(Version::GetVersionFromString(
   1057                                     version_info.Version()));
   1058         }
   1059       }
   1060       scoped_ptr<Version> browser_min_version(
   1061           Version::GetVersionFromString(update->browser_min_version));
   1062       if (browser_version.get() && browser_min_version.get() &&
   1063           browser_min_version->CompareTo(*browser_version.get()) > 0) {
   1064         // TODO(asargent) - We may want this to show up in the extensions UI
   1065         // eventually. (http://crbug.com/12547).
   1066         LOG(WARNING) << "Updated version of extension " << update->extension_id
   1067                      << " available, but requires chrome version "
   1068                      << update->browser_min_version;
   1069         continue;
   1070       }
   1071     }
   1072     result.push_back(i);
   1073   }
   1074   return result;
   1075 }
   1076 
   1077 void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
   1078   AddToInProgress(fetch_data->extension_ids());
   1079 
   1080   scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
   1081   if (CommandLine::ForCurrentProcess()->HasSwitch(
   1082       switches::kDisableBackgroundNetworking))
   1083     return;
   1084 
   1085   std::deque<ManifestFetchData*>::const_iterator i;
   1086   for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
   1087     if (fetch_data->full_url() == (*i)->full_url()) {
   1088       // This url is already scheduled to be fetched.
   1089       return;
   1090     }
   1091   }
   1092 
   1093   if (manifest_fetcher_.get() != NULL) {
   1094     if (manifest_fetcher_->url() != fetch_data->full_url()) {
   1095       manifests_pending_.push_back(scoped_fetch_data.release());
   1096     }
   1097   } else {
   1098     UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
   1099         fetch_data->full_url().possibly_invalid_spec().length());
   1100 
   1101     current_manifest_fetch_.swap(scoped_fetch_data);
   1102     manifest_fetcher_.reset(
   1103         URLFetcher::Create(kManifestFetcherId, fetch_data->full_url(),
   1104                            URLFetcher::GET, this));
   1105     manifest_fetcher_->set_request_context(
   1106         profile_->GetRequestContext());
   1107     manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
   1108                                       net::LOAD_DO_NOT_SAVE_COOKIES |
   1109                                       net::LOAD_DISABLE_CACHE);
   1110     manifest_fetcher_->Start();
   1111   }
   1112 }
   1113 
   1114 void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
   1115                                              const GURL& url,
   1116                                              const std::string& hash,
   1117                                              const std::string& version) {
   1118   for (std::deque<ExtensionFetch>::const_iterator iter =
   1119            extensions_pending_.begin();
   1120        iter != extensions_pending_.end(); ++iter) {
   1121     if (iter->id == id || iter->url == url) {
   1122       return;  // already scheduled
   1123     }
   1124   }
   1125 
   1126   if (extension_fetcher_.get() != NULL) {
   1127     if (extension_fetcher_->url() != url) {
   1128       extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
   1129     }
   1130   } else {
   1131     extension_fetcher_.reset(
   1132         URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this));
   1133     extension_fetcher_->set_request_context(
   1134         profile_->GetRequestContext());
   1135     extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
   1136                                        net::LOAD_DO_NOT_SAVE_COOKIES |
   1137                                        net::LOAD_DISABLE_CACHE);
   1138     extension_fetcher_->Start();
   1139     current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
   1140   }
   1141 }
   1142 
   1143 void ExtensionUpdater::NotifyStarted() {
   1144   NotificationService::current()->Notify(
   1145       NotificationType::EXTENSION_UPDATING_STARTED,
   1146       Source<Profile>(profile_),
   1147       NotificationService::NoDetails());
   1148 }
   1149 
   1150 void ExtensionUpdater::NotifyUpdateFound(const std::string& extension_id) {
   1151   NotificationService::current()->Notify(
   1152       NotificationType::EXTENSION_UPDATE_FOUND,
   1153       Source<Profile>(profile_),
   1154       Details<const std::string>(&extension_id));
   1155 }
   1156 
   1157 void ExtensionUpdater::NotifyIfFinished() {
   1158   if (in_progress_ids_.empty()) {
   1159     NotificationService::current()->Notify(
   1160         NotificationType::EXTENSION_UPDATING_FINISHED,
   1161         Source<Profile>(profile_),
   1162         NotificationService::NoDetails());
   1163     VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
   1164   }
   1165 }
   1166 
   1167 void ExtensionUpdater::AddToInProgress(const std::set<std::string>& ids) {
   1168   std::set<std::string>::const_iterator i;
   1169   for (i = ids.begin(); i != ids.end(); ++i)
   1170     in_progress_ids_.insert(*i);
   1171 }
   1172 
   1173 void ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) {
   1174   std::set<std::string>::const_iterator i;
   1175   for (i = ids.begin(); i != ids.end(); ++i)
   1176     in_progress_ids_.erase(*i);
   1177 }
   1178