Home | History | Annotate | Download | only in updater
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/extensions/updater/extension_updater.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/logging.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/rand_util.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_split.h"
     19 #include "chrome/browser/chrome_notification_types.h"
     20 #include "chrome/browser/extensions/api/module/module.h"
     21 #include "chrome/browser/extensions/blacklist.h"
     22 #include "chrome/browser/extensions/crx_installer.h"
     23 #include "chrome/browser/extensions/extension_service.h"
     24 #include "chrome/browser/extensions/pending_extension_manager.h"
     25 #include "chrome/browser/extensions/updater/extension_downloader.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/common/extensions/extension.h"
     28 #include "chrome/common/extensions/extension_set.h"
     29 #include "chrome/common/extensions/manifest.h"
     30 #include "chrome/common/pref_names.h"
     31 #include "content/public/browser/browser_thread.h"
     32 #include "content/public/browser/notification_details.h"
     33 #include "content/public/browser/notification_service.h"
     34 #include "content/public/browser/notification_source.h"
     35 #include "crypto/sha2.h"
     36 
     37 using base::RandDouble;
     38 using base::RandInt;
     39 using base::Time;
     40 using base::TimeDelta;
     41 using content::BrowserThread;
     42 using prefs::kExtensionBlacklistUpdateVersion;
     43 using prefs::kLastExtensionsUpdateCheck;
     44 using prefs::kNextExtensionsUpdateCheck;
     45 
     46 typedef extensions::ExtensionDownloaderDelegate::Error Error;
     47 typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
     48 
     49 namespace {
     50 
     51 // Wait at least 5 minutes after browser startup before we do any checks. If you
     52 // change this value, make sure to update comments where it is used.
     53 const int kStartupWaitSeconds = 60 * 5;
     54 
     55 // For sanity checking on update frequency - enforced in release mode only.
     56 const int kMinUpdateFrequencySeconds = 30;
     57 const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
     58 
     59 // Require at least 5 seconds between consecutive non-succesful extension update
     60 // checks.
     61 const int kMinUpdateThrottleTime = 5;
     62 
     63 // When we've computed a days value, we want to make sure we don't send a
     64 // negative value (due to the system clock being set backwards, etc.), since -1
     65 // is a special sentinel value that means "never pinged", and other negative
     66 // values don't make sense.
     67 int SanitizeDays(int days) {
     68   if (days < 0)
     69     return 0;
     70   return days;
     71 }
     72 
     73 // Calculates the value to use for the ping days parameter.
     74 int CalculatePingDays(const Time& last_ping_day) {
     75   int days = extensions::ManifestFetchData::kNeverPinged;
     76   if (!last_ping_day.is_null()) {
     77     days = SanitizeDays((Time::Now() - last_ping_day).InDays());
     78   }
     79   return days;
     80 }
     81 
     82 int CalculateActivePingDays(const Time& last_active_ping_day,
     83                             bool hasActiveBit) {
     84   if (!hasActiveBit)
     85     return 0;
     86   if (last_active_ping_day.is_null())
     87     return extensions::ManifestFetchData::kNeverPinged;
     88   return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
     89 }
     90 
     91 }  // namespace
     92 
     93 namespace extensions {
     94 
     95 ExtensionUpdater::CheckParams::CheckParams()
     96     : check_blacklist(true), install_immediately(false) {}
     97 
     98 ExtensionUpdater::CheckParams::~CheckParams() {}
     99 
    100 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
    101     const std::string& i,
    102     const base::FilePath& p,
    103     const GURL& u,
    104     const std::set<int>& request_ids)
    105     : extension_id(i),
    106       path(p),
    107       download_url(u),
    108       request_ids(request_ids) {}
    109 
    110 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile() : path(), download_url() {}
    111 
    112 ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
    113 
    114 ExtensionUpdater::InProgressCheck::InProgressCheck()
    115     : install_immediately(false) {}
    116 
    117 ExtensionUpdater::InProgressCheck::~InProgressCheck() {}
    118 
    119 struct ExtensionUpdater::ThrottleInfo {
    120   ThrottleInfo()
    121       : in_progress(true),
    122         throttle_delay(kMinUpdateThrottleTime),
    123         check_start(Time::Now()) {}
    124 
    125   bool in_progress;
    126   int throttle_delay;
    127   Time check_start;
    128 };
    129 
    130 ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
    131                                    ExtensionPrefs* extension_prefs,
    132                                    PrefService* prefs,
    133                                    Profile* profile,
    134                                    Blacklist* blacklist,
    135                                    int frequency_seconds)
    136     : alive_(false),
    137       weak_ptr_factory_(this),
    138       service_(service), frequency_seconds_(frequency_seconds),
    139       will_check_soon_(false), extension_prefs_(extension_prefs),
    140       prefs_(prefs), profile_(profile), blacklist_(blacklist),
    141       next_request_id_(0),
    142       crx_install_is_running_(false) {
    143   DCHECK_GE(frequency_seconds_, 5);
    144   DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
    145 #ifdef NDEBUG
    146   // In Release mode we enforce that update checks don't happen too often.
    147   frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
    148 #endif
    149   frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
    150 
    151   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
    152                  content::NotificationService::AllBrowserContextsAndSources());
    153 }
    154 
    155 ExtensionUpdater::~ExtensionUpdater() {
    156   Stop();
    157 }
    158 
    159 // The overall goal here is to balance keeping clients up to date while
    160 // avoiding a thundering herd against update servers.
    161 TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
    162   DCHECK(alive_);
    163   // If someone's testing with a quick frequency, just allow it.
    164   if (frequency_seconds_ < kStartupWaitSeconds)
    165     return TimeDelta::FromSeconds(frequency_seconds_);
    166 
    167   // If we've never scheduled a check before, start at frequency_seconds_.
    168   if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
    169     return TimeDelta::FromSeconds(frequency_seconds_);
    170 
    171   // If it's been a long time since our last actual check, we want to do one
    172   // relatively soon.
    173   Time now = Time::Now();
    174   Time last = Time::FromInternalValue(prefs_->GetInt64(
    175       kLastExtensionsUpdateCheck));
    176   int days = (now - last).InDays();
    177   if (days >= 30) {
    178     // Wait 5-10 minutes.
    179     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
    180                                           kStartupWaitSeconds * 2));
    181   } else if (days >= 14) {
    182     // Wait 10-20 minutes.
    183     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
    184                                           kStartupWaitSeconds * 4));
    185   } else if (days >= 3) {
    186     // Wait 20-40 minutes.
    187     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
    188                                           kStartupWaitSeconds * 8));
    189   }
    190 
    191   // Read the persisted next check time, and use that if it isn't too soon.
    192   // Otherwise pick something random.
    193   Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
    194       kNextExtensionsUpdateCheck));
    195   Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
    196   if (saved_next >= earliest) {
    197     return saved_next - now;
    198   } else {
    199     return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
    200                                           frequency_seconds_));
    201   }
    202 }
    203 
    204 void ExtensionUpdater::Start() {
    205   DCHECK(!alive_);
    206   // If these are NULL, then that means we've been called after Stop()
    207   // has been called.
    208   DCHECK(service_);
    209   DCHECK(extension_prefs_);
    210   DCHECK(prefs_);
    211   DCHECK(profile_);
    212   DCHECK(!weak_ptr_factory_.HasWeakPtrs());
    213   alive_ = true;
    214   // Make sure our prefs are registered, then schedule the first check.
    215   ScheduleNextCheck(DetermineFirstCheckDelay());
    216 }
    217 
    218 void ExtensionUpdater::Stop() {
    219   weak_ptr_factory_.InvalidateWeakPtrs();
    220   alive_ = false;
    221   service_ = NULL;
    222   extension_prefs_ = NULL;
    223   prefs_ = NULL;
    224   profile_ = NULL;
    225   timer_.Stop();
    226   will_check_soon_ = false;
    227   downloader_.reset();
    228 }
    229 
    230 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
    231   DCHECK(alive_);
    232   DCHECK(!timer_.IsRunning());
    233   DCHECK(target_delay >= TimeDelta::FromSeconds(1));
    234 
    235   // Add +/- 10% random jitter.
    236   double delay_ms = target_delay.InMillisecondsF();
    237   double jitter_factor = (RandDouble() * .2) - 0.1;
    238   delay_ms += delay_ms * jitter_factor;
    239   TimeDelta actual_delay = TimeDelta::FromMilliseconds(
    240       static_cast<int64>(delay_ms));
    241 
    242   // Save the time of next check.
    243   Time next = Time::Now() + actual_delay;
    244   prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
    245 
    246   timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
    247 }
    248 
    249 void ExtensionUpdater::TimerFired() {
    250   DCHECK(alive_);
    251   CheckNow(default_params_);
    252 
    253   // If the user has overridden the update frequency, don't bother reporting
    254   // this.
    255   if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
    256     Time last = Time::FromInternalValue(prefs_->GetInt64(
    257         kLastExtensionsUpdateCheck));
    258     if (last.ToInternalValue() != 0) {
    259       // Use counts rather than time so we can use minutes rather than millis.
    260       UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
    261           (Time::Now() - last).InMinutes(),
    262           TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
    263           TimeDelta::FromDays(40).InMinutes(),
    264           50);  // 50 buckets seems to be the default.
    265     }
    266   }
    267 
    268   // Save the last check time, and schedule the next check.
    269   int64 now = Time::Now().ToInternalValue();
    270   prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
    271   ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
    272 }
    273 
    274 void ExtensionUpdater::CheckSoon() {
    275   DCHECK(alive_);
    276   if (will_check_soon_)
    277     return;
    278   if (BrowserThread::PostTask(
    279           BrowserThread::UI, FROM_HERE,
    280           base::Bind(&ExtensionUpdater::DoCheckSoon,
    281                      weak_ptr_factory_.GetWeakPtr()))) {
    282     will_check_soon_ = true;
    283   } else {
    284     NOTREACHED();
    285   }
    286 }
    287 
    288 bool ExtensionUpdater::WillCheckSoon() const {
    289   return will_check_soon_;
    290 }
    291 
    292 void ExtensionUpdater::DoCheckSoon() {
    293   DCHECK(will_check_soon_);
    294   CheckNow(default_params_);
    295   will_check_soon_ = false;
    296 }
    297 
    298 void ExtensionUpdater::AddToDownloader(
    299     const ExtensionSet* extensions,
    300     const std::list<std::string>& pending_ids,
    301     int request_id) {
    302   InProgressCheck& request = requests_in_progress_[request_id];
    303   for (ExtensionSet::const_iterator extension_iter = extensions->begin();
    304        extension_iter != extensions->end(); ++extension_iter) {
    305     const Extension& extension = *extension_iter->get();
    306     if (!Manifest::IsAutoUpdateableLocation(extension.location())) {
    307       VLOG(2) << "Extension " << extension.id() << " is not auto updateable";
    308       continue;
    309     }
    310     // An extension might be overwritten by policy, and have its update url
    311     // changed. Make sure existing extensions aren't fetched again, if a
    312     // pending fetch for an extension with the same id already exists.
    313     std::list<std::string>::const_iterator pending_id_iter = std::find(
    314         pending_ids.begin(), pending_ids.end(), extension.id());
    315     if (pending_id_iter == pending_ids.end()) {
    316       if (downloader_->AddExtension(extension, request_id))
    317         request.in_progress_ids_.push_back(extension.id());
    318     }
    319   }
    320 }
    321 
    322 void ExtensionUpdater::CheckNow(const CheckParams& params) {
    323   int request_id = next_request_id_++;
    324 
    325   VLOG(2) << "Starting update check " << request_id;
    326   if (params.ids.empty())
    327     NotifyStarted();
    328 
    329   DCHECK(alive_);
    330 
    331   InProgressCheck& request = requests_in_progress_[request_id];
    332   request.callback = params.callback;
    333   request.install_immediately = params.install_immediately;
    334 
    335   if (!downloader_.get()) {
    336     downloader_.reset(
    337         new ExtensionDownloader(this, profile_->GetRequestContext()));
    338   }
    339 
    340   // Add fetch records for extensions that should be fetched by an update URL.
    341   // These extensions are not yet installed. They come from group policy
    342   // and external install sources.
    343   const PendingExtensionManager* pending_extension_manager =
    344       service_->pending_extension_manager();
    345 
    346   std::list<std::string> pending_ids;
    347 
    348   if (params.ids.empty()) {
    349     // If no extension ids are specified, check for updates for all extensions.
    350     pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
    351 
    352     std::list<std::string>::const_iterator iter;
    353     for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
    354       const PendingExtensionInfo* info = pending_extension_manager->GetById(
    355           *iter);
    356       if (!Manifest::IsAutoUpdateableLocation(info->install_source())) {
    357         VLOG(2) << "Extension " << *iter << " is not auto updateable";
    358         continue;
    359       }
    360       if (downloader_->AddPendingExtension(*iter, info->update_url(),
    361                                            request_id))
    362         request.in_progress_ids_.push_back(*iter);
    363     }
    364 
    365     AddToDownloader(service_->extensions(), pending_ids, request_id);
    366     AddToDownloader(service_->disabled_extensions(), pending_ids, request_id);
    367   } else {
    368     for (std::list<std::string>::const_iterator it = params.ids.begin();
    369          it != params.ids.end(); ++it) {
    370       const Extension* extension = service_->GetExtensionById(*it, true);
    371       DCHECK(extension);
    372       if (downloader_->AddExtension(*extension, request_id))
    373         request.in_progress_ids_.push_back(extension->id());
    374     }
    375   }
    376 
    377   // Start a fetch of the blacklist if needed.
    378   if (params.check_blacklist) {
    379     ManifestFetchData::PingData ping_data;
    380     ping_data.rollcall_days =
    381         CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
    382     request.in_progress_ids_.push_back(ExtensionDownloader::kBlacklistAppID);
    383     downloader_->StartBlacklistUpdate(
    384         prefs_->GetString(kExtensionBlacklistUpdateVersion), ping_data,
    385         request_id);
    386   }
    387 
    388   // StartAllPending() might call OnExtensionDownloadFailed/Finished before
    389   // it returns, which would cause NotifyIfFinished to incorrectly try to
    390   // send out a notification. So check before we call StartAllPending if any
    391   // extensions are going to be updated, and use that to figure out if
    392   // NotifyIfFinished should be called.
    393   bool noChecks = request.in_progress_ids_.empty();
    394 
    395   // StartAllPending() will call OnExtensionDownloadFailed or
    396   // OnExtensionDownloadFinished for each extension that was checked.
    397   downloader_->StartAllPending();
    398 
    399 
    400   if (noChecks)
    401     NotifyIfFinished(request_id);
    402 }
    403 
    404 bool ExtensionUpdater::CheckExtensionSoon(const std::string& extension_id,
    405                                           const FinishedCallback& callback) {
    406   bool have_throttle_info = ContainsKey(throttle_info_, extension_id);
    407   ThrottleInfo& info = throttle_info_[extension_id];
    408   if (have_throttle_info) {
    409     // We already had a ThrottleInfo object for this extension, check if the
    410     // update check request should be allowed.
    411 
    412     // If another check is in progress, don't start a new check.
    413     if (info.in_progress)
    414       return false;
    415 
    416     Time now = Time::Now();
    417     Time last = info.check_start;
    418     // If somehow time moved back, we don't want to infinitely keep throttling.
    419     if (now < last) {
    420       last = now;
    421       info.check_start = now;
    422     }
    423     Time earliest = last + TimeDelta::FromSeconds(info.throttle_delay);
    424     // If check is too soon, throttle.
    425     if (now < earliest)
    426       return false;
    427 
    428     // TODO(mek): Somehow increase time between allowing checks when checks
    429     // are repeatedly throttled and don't result in updates being installed.
    430 
    431     // It's okay to start a check, update values.
    432     info.check_start = now;
    433     info.in_progress = true;
    434   }
    435 
    436   CheckParams params;
    437   params.ids.push_back(extension_id);
    438   params.check_blacklist = false;
    439   params.callback = base::Bind(&ExtensionUpdater::ExtensionCheckFinished,
    440                                weak_ptr_factory_.GetWeakPtr(),
    441                                extension_id, callback);
    442   CheckNow(params);
    443   return true;
    444 }
    445 
    446 void ExtensionUpdater::ExtensionCheckFinished(
    447     const std::string& extension_id,
    448     const FinishedCallback& callback) {
    449   std::map<std::string, ThrottleInfo>::iterator it =
    450       throttle_info_.find(extension_id);
    451   if (it != throttle_info_.end()) {
    452     it->second.in_progress = false;
    453   }
    454   callback.Run();
    455 }
    456 
    457 void ExtensionUpdater::OnExtensionDownloadFailed(
    458     const std::string& id,
    459     Error error,
    460     const PingResult& ping,
    461     const std::set<int>& request_ids) {
    462   DCHECK(alive_);
    463   UpdatePingData(id, ping);
    464   bool install_immediately = false;
    465   for (std::set<int>::const_iterator it = request_ids.begin();
    466        it != request_ids.end(); ++it) {
    467     InProgressCheck& request = requests_in_progress_[*it];
    468     install_immediately |= request.install_immediately;
    469     request.in_progress_ids_.remove(id);
    470     NotifyIfFinished(*it);
    471   }
    472 
    473   // This method is called if no updates were found. However a previous update
    474   // check might have queued an update for this extension already. If a
    475   // current update check has |install_immediately| set the previously
    476   // queued update should be installed now.
    477   if (install_immediately && service_->GetPendingExtensionUpdate(id))
    478     service_->FinishDelayedInstallation(id);
    479 }
    480 
    481 void ExtensionUpdater::OnExtensionDownloadFinished(
    482     const std::string& id,
    483     const base::FilePath& path,
    484     const GURL& download_url,
    485     const std::string& version,
    486     const PingResult& ping,
    487     const std::set<int>& request_ids) {
    488   DCHECK(alive_);
    489   UpdatePingData(id, ping);
    490 
    491   VLOG(2) << download_url << " written to " << path.value();
    492 
    493   FetchedCRXFile fetched(id, path, download_url, request_ids);
    494   fetched_crx_files_.push(fetched);
    495 
    496   // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
    497   // starting the crx installer.
    498   MaybeInstallCRXFile();
    499 }
    500 
    501 void ExtensionUpdater::OnBlacklistDownloadFinished(
    502     const std::string& data,
    503     const std::string& package_hash,
    504     const std::string& version,
    505     const PingResult& ping,
    506     const std::set<int>& request_ids) {
    507   DCHECK(alive_);
    508   UpdatePingData(ExtensionDownloader::kBlacklistAppID, ping);
    509   for (std::set<int>::const_iterator it = request_ids.begin();
    510        it != request_ids.end(); ++it) {
    511     InProgressCheck& request = requests_in_progress_[*it];
    512     request.in_progress_ids_.remove(ExtensionDownloader::kBlacklistAppID);
    513     NotifyIfFinished(*it);
    514   }
    515 
    516   // Verify sha256 hash value.
    517   char sha256_hash_value[crypto::kSHA256Length];
    518   crypto::SHA256HashString(data, sha256_hash_value, crypto::kSHA256Length);
    519   std::string hash_in_hex = base::HexEncode(sha256_hash_value,
    520                                             crypto::kSHA256Length);
    521 
    522   if (package_hash != hash_in_hex) {
    523     NOTREACHED() << "Fetched blacklist checksum is not as expected. "
    524         << "Expected: " << package_hash << " Actual: " << hash_in_hex;
    525     return;
    526   }
    527   std::vector<std::string> blacklist;
    528   base::SplitString(data, '\n', &blacklist);
    529 
    530   blacklist_->SetFromUpdater(blacklist, version);
    531 }
    532 
    533 bool ExtensionUpdater::GetPingDataForExtension(
    534     const std::string& id,
    535     ManifestFetchData::PingData* ping_data) {
    536   DCHECK(alive_);
    537   ping_data->rollcall_days = CalculatePingDays(
    538       extension_prefs_->LastPingDay(id));
    539   ping_data->is_enabled = service_->IsExtensionEnabled(id);
    540   ping_data->active_days =
    541       CalculateActivePingDays(extension_prefs_->LastActivePingDay(id),
    542                               extension_prefs_->GetActiveBit(id));
    543   return true;
    544 }
    545 
    546 std::string ExtensionUpdater::GetUpdateUrlData(const std::string& id) {
    547   DCHECK(alive_);
    548   return extension::GetUpdateURLData(extension_prefs_, id);
    549 }
    550 
    551 bool ExtensionUpdater::IsExtensionPending(const std::string& id) {
    552   DCHECK(alive_);
    553   return service_->pending_extension_manager()->IsIdPending(id);
    554 }
    555 
    556 bool ExtensionUpdater::GetExtensionExistingVersion(const std::string& id,
    557                                                    std::string* version) {
    558   DCHECK(alive_);
    559   if (id == ExtensionDownloader::kBlacklistAppID) {
    560     *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
    561     return true;
    562   }
    563   const Extension* extension = service_->GetExtensionById(id, true);
    564   if (!extension)
    565     return false;
    566   const Extension* update = service_->GetPendingExtensionUpdate(id);
    567   if (update)
    568     *version = update->VersionString();
    569   else
    570     *version = extension->VersionString();
    571   return true;
    572 }
    573 
    574 void ExtensionUpdater::UpdatePingData(const std::string& id,
    575                                       const PingResult& ping_result) {
    576   DCHECK(alive_);
    577   if (ping_result.did_ping) {
    578     if (id == ExtensionDownloader::kBlacklistAppID) {
    579       extension_prefs_->SetBlacklistLastPingDay(ping_result.day_start);
    580     } else if (service_->GetExtensionById(id, true) != NULL) {
    581       extension_prefs_->SetLastPingDay(id, ping_result.day_start);
    582     }
    583   }
    584   if (extension_prefs_->GetActiveBit(id)) {
    585     extension_prefs_->SetActiveBit(id, false);
    586     extension_prefs_->SetLastActivePingDay(id, ping_result.day_start);
    587   }
    588 }
    589 
    590 void ExtensionUpdater::MaybeInstallCRXFile() {
    591   if (crx_install_is_running_ || fetched_crx_files_.empty())
    592     return;
    593 
    594   std::set<int> request_ids;
    595 
    596   while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
    597     const FetchedCRXFile& crx_file = fetched_crx_files_.top();
    598 
    599     VLOG(2) << "updating " << crx_file.extension_id
    600             << " with " << crx_file.path.value();
    601 
    602     // The ExtensionService is now responsible for cleaning up the temp file
    603     // at |crx_file.path|.
    604     CrxInstaller* installer = NULL;
    605     if (service_->UpdateExtension(crx_file.extension_id,
    606                                   crx_file.path,
    607                                   crx_file.download_url,
    608                                   &installer)) {
    609       crx_install_is_running_ = true;
    610       current_crx_file_ = crx_file;
    611 
    612       for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
    613           it != crx_file.request_ids.end(); ++it) {
    614         InProgressCheck& request = requests_in_progress_[*it];
    615         if (request.install_immediately) {
    616           installer->set_install_wait_for_idle(false);
    617           break;
    618         }
    619       }
    620 
    621       // Source parameter ensures that we only see the completion event for the
    622       // the installer we started.
    623       registrar_.Add(this,
    624                      chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    625                      content::Source<CrxInstaller>(installer));
    626     } else {
    627       for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
    628            it != crx_file.request_ids.end(); ++it) {
    629         InProgressCheck& request = requests_in_progress_[*it];
    630         request.in_progress_ids_.remove(crx_file.extension_id);
    631       }
    632       request_ids.insert(crx_file.request_ids.begin(),
    633                          crx_file.request_ids.end());
    634     }
    635     fetched_crx_files_.pop();
    636   }
    637 
    638   for (std::set<int>::const_iterator it = request_ids.begin();
    639        it != request_ids.end(); ++it) {
    640     NotifyIfFinished(*it);
    641   }
    642 }
    643 
    644 void ExtensionUpdater::Observe(int type,
    645                                const content::NotificationSource& source,
    646                                const content::NotificationDetails& details) {
    647   switch (type) {
    648     case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
    649       // No need to listen for CRX_INSTALLER_DONE anymore.
    650       registrar_.Remove(this,
    651                         chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    652                         source);
    653       crx_install_is_running_ = false;
    654 
    655       const FetchedCRXFile& crx_file = current_crx_file_;
    656       for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
    657           it != crx_file.request_ids.end(); ++it) {
    658         InProgressCheck& request = requests_in_progress_[*it];
    659         request.in_progress_ids_.remove(crx_file.extension_id);
    660         NotifyIfFinished(*it);
    661       }
    662 
    663       // If any files are available to update, start one.
    664       MaybeInstallCRXFile();
    665       break;
    666     }
    667     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
    668       const Extension* extension =
    669           content::Details<const InstalledExtensionInfo>(details)->extension;
    670       if (extension)
    671         throttle_info_.erase(extension->id());
    672       break;
    673     }
    674     default:
    675       NOTREACHED();
    676   }
    677 }
    678 
    679 void ExtensionUpdater::NotifyStarted() {
    680   content::NotificationService::current()->Notify(
    681       chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED,
    682       content::Source<Profile>(profile_),
    683       content::NotificationService::NoDetails());
    684 }
    685 
    686 void ExtensionUpdater::NotifyIfFinished(int request_id) {
    687   DCHECK(ContainsKey(requests_in_progress_, request_id));
    688   const InProgressCheck& request = requests_in_progress_[request_id];
    689   if (request.in_progress_ids_.empty()) {
    690     VLOG(2) << "Finished update check " << request_id;
    691     if (!request.callback.is_null())
    692       request.callback.Run();
    693     requests_in_progress_.erase(request_id);
    694   }
    695 }
    696 
    697 }  // namespace extensions
    698