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