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