extension_updater.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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/logging.h" 11#include "base/file_util.h" 12#include "base/file_version_info.h" 13#include "base/histogram.h" 14#include "base/rand_util.h" 15#include "base/sha2.h" 16#include "base/stl_util-inl.h" 17#include "base/string_util.h" 18#include "base/time.h" 19#include "base/thread.h" 20#include "base/version.h" 21#include "chrome/browser/browser_process.h" 22#include "chrome/browser/extensions/extension_error_reporter.h" 23#include "chrome/browser/extensions/extensions_service.h" 24#include "chrome/browser/pref_service.h" 25#include "chrome/browser/profile.h" 26#include "chrome/browser/utility_process_host.h" 27#include "chrome/common/chrome_switches.h" 28#include "chrome/common/chrome_version_info.h" 29#include "chrome/common/extensions/extension.h" 30#include "chrome/common/extensions/extension_constants.h" 31#include "chrome/common/pref_names.h" 32#include "googleurl/src/gurl.h" 33#include "net/base/escape.h" 34#include "net/base/load_flags.h" 35#include "net/url_request/url_request_status.h" 36 37#if defined(OS_WIN) 38#include "base/registry.h" 39#elif defined(OS_MACOSX) 40#include "base/sys_string_conversions.h" 41#endif 42 43using base::RandDouble; 44using base::RandInt; 45using base::Time; 46using base::TimeDelta; 47using prefs::kExtensionBlacklistUpdateVersion; 48using prefs::kLastExtensionsUpdateCheck; 49using prefs::kNextExtensionsUpdateCheck; 50 51// NOTE: HTTPS is used here to ensure the response from omaha can be trusted. 52// The response contains a url for fetching the blacklist and a hash value 53// for validation. 54const char* ExtensionUpdater::kBlacklistUpdateUrl = 55 "https://clients2.google.com/service/update2/crx"; 56 57// Update AppID for extension blacklist. 58const char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist"; 59 60// Wait at least 5 minutes after browser startup before we do any checks. If you 61// change this value, make sure to update comments where it is used. 62const int kStartupWaitSeconds = 60 * 5; 63 64// For sanity checking on update frequency - enforced in release mode only. 65static const int kMinUpdateFrequencySeconds = 30; 66static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days 67 68// Maximum length of an extension manifest update check url, since it is a GET 69// request. We want to stay under 2K because of proxies, etc. 70static const int kExtensionsManifestMaxURLSize = 2000; 71 72 73// The format for request parameters in update checks is: 74// 75// ?x=EXT1_INFO&x=EXT2_INFO 76// 77// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form: 78// 79// id=EXTENSION_ID&v=VERSION&uc 80// 81// Additionally, we may include the parameter ping=PING_DATA where PING_DATA 82// looks like r=DAYS for extensions in the Chrome extensions gallery. This value 83// will be present at most once every 24 hours, and indicate the number of days 84// since the last time it was present in an update check. 85// 86// So for two extensions like: 87// Extension 1- id:aaaa version:1.1 88// Extension 2- id:bbbb version:2.0 89// 90// the full update url would be: 91// http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc 92// 93// (Note that '=' is %3D and '&' is %26 when urlencoded.) 94bool ManifestFetchData::AddExtension(std::string id, std::string version, 95 int days) { 96 if (extension_ids_.find(id) != extension_ids_.end()) { 97 NOTREACHED() << "Duplicate extension id " << id; 98 return false; 99 } 100 101 // Compute the string we'd append onto the full_url_, and see if it fits. 102 std::vector<std::string> parts; 103 parts.push_back("id=" + id); 104 parts.push_back("v=" + version); 105 parts.push_back("uc"); 106 107 if (ShouldPing(days)) { 108 parts.push_back("ping=" + EscapeQueryParamValue("r=" + IntToString(days), 109 true)); 110 } 111 112 std::string extra = full_url_.has_query() ? "&" : "?"; 113 extra += "x=" + EscapeQueryParamValue(JoinString(parts, '&'), true); 114 115 // Check against our max url size, exempting the first extension added. 116 int new_size = full_url_.possibly_invalid_spec().size() + extra.size(); 117 if (extension_ids_.size() > 0 && new_size > kExtensionsManifestMaxURLSize) { 118 UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1); 119 return false; 120 } 121 UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0); 122 123 // We have room so go ahead and add the extension. 124 extension_ids_.insert(id); 125 ping_days_[id] = days; 126 full_url_ = GURL(full_url_.possibly_invalid_spec() + extra); 127 return true; 128} 129 130bool ManifestFetchData::DidPing(std::string extension_id) const { 131 std::map<std::string, int>::const_iterator i = ping_days_.find(extension_id); 132 if (i != ping_days_.end()) { 133 return ShouldPing(i->second); 134 } 135 return false; 136} 137 138bool ManifestFetchData::ShouldPing(int days) const { 139 return base_url_.DomainIs("google.com") && 140 (days == kNeverPinged || days > 0); 141} 142 143namespace { 144 145// Calculates the value to use for the ping days parameter. 146static int CalculatePingDays(const Time& last_ping_day) { 147 int days = ManifestFetchData::kNeverPinged; 148 if (!last_ping_day.is_null()) { 149 days = (Time::Now() - last_ping_day).InDays(); 150 } 151 return days; 152} 153 154} // namespace 155 156ManifestFetchesBuilder::ManifestFetchesBuilder( 157 ExtensionUpdateService* service) : service_(service) { 158 DCHECK(service_); 159} 160 161void ManifestFetchesBuilder::AddExtension(const Extension& extension) { 162 AddExtensionData(extension.location(), 163 extension.id(), 164 *extension.version(), 165 extension.is_theme(), 166 extension.update_url()); 167} 168 169void ManifestFetchesBuilder::AddPendingExtension( 170 const std::string& id, 171 const PendingExtensionInfo& info) { 172 // Use a zero version to ensure that a pending extension will always 173 // be updated, and thus installed (assuming all extensions have 174 // non-zero versions). 175 scoped_ptr<Version> version( 176 Version::GetVersionFromString("0.0.0.0")); 177 AddExtensionData(Extension::INTERNAL, id, *version, 178 info.is_theme, info.update_url); 179} 180 181void ManifestFetchesBuilder::ReportStats() const { 182 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtensions", 183 url_stats_.google_url_count + 184 url_stats_.other_url_count - 185 url_stats_.theme_count); 186 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme", 187 url_stats_.theme_count); 188 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl", 189 url_stats_.google_url_count); 190 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl", 191 url_stats_.other_url_count); 192 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl", 193 url_stats_.no_url_count); 194} 195 196std::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() { 197 std::vector<ManifestFetchData*> fetches; 198 fetches.reserve(fetches_.size()); 199 for (std::multimap<GURL, ManifestFetchData*>::iterator it = 200 fetches_.begin(); it != fetches_.end(); ++it) { 201 fetches.push_back(it->second); 202 } 203 fetches_.clear(); 204 url_stats_ = URLStats(); 205 return fetches; 206} 207 208void ManifestFetchesBuilder::AddExtensionData( 209 Extension::Location location, 210 const std::string& id, 211 const Version& version, 212 bool is_theme, 213 GURL update_url) { 214 // Only internal and external extensions can be autoupdated. 215 if (location != Extension::INTERNAL && 216 !Extension::IsExternalLocation(location)) { 217 return; 218 } 219 220 // Skip extensions with non-empty invalid update URLs. 221 if (!update_url.is_empty() && !update_url.is_valid()) { 222 LOG(WARNING) << "Extension " << id << " has invalid update url " 223 << update_url; 224 return; 225 } 226 227 // Skip extensions with empty IDs. 228 if (id.empty()) { 229 LOG(WARNING) << "Found extension with empty ID"; 230 return; 231 } 232 233 if (update_url.DomainIs("google.com")) { 234 url_stats_.google_url_count++; 235 } else if (update_url.is_empty()) { 236 url_stats_.no_url_count++; 237 // Fill in default update URL. 238 // 239 // TODO(akalin): Figure out if we should use the HTTPS version. 240 update_url = GURL(extension_urls::kGalleryUpdateHttpUrl); 241 } else { 242 url_stats_.other_url_count++; 243 } 244 245 if (is_theme) { 246 url_stats_.theme_count++; 247 } 248 249 DCHECK(!update_url.is_empty()); 250 DCHECK(update_url.is_valid()); 251 252 ManifestFetchData* fetch = NULL; 253 std::multimap<GURL, ManifestFetchData*>::iterator existing_iter = 254 fetches_.find(update_url); 255 256 // Find or create a ManifestFetchData to add this extension to. 257 int ping_days = 258 CalculatePingDays(service_->extension_prefs()->LastPingDay(id)); 259 while (existing_iter != fetches_.end()) { 260 if (existing_iter->second->AddExtension(id, version.GetString(), 261 ping_days)) { 262 fetch = existing_iter->second; 263 break; 264 } 265 existing_iter++; 266 } 267 if (!fetch) { 268 fetch = new ManifestFetchData(update_url); 269 fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch)); 270 bool added = fetch->AddExtension(id, version.GetString(), ping_days); 271 DCHECK(added); 272 } 273} 274 275// A utility class to do file handling on the file I/O thread. 276class ExtensionUpdaterFileHandler 277 : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> { 278 public: 279 // Writes crx file data into a tempfile, and calls back the updater. 280 void WriteTempFile(const std::string& extension_id, const std::string& data, 281 const GURL& download_url, 282 scoped_refptr<ExtensionUpdater> updater) { 283 // Make sure we're running in the right thread. 284 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); 285 286 FilePath path; 287 if (!file_util::CreateTemporaryFile(&path)) { 288 LOG(WARNING) << "Failed to create temporary file path"; 289 return; 290 } 291 if (file_util::WriteFile(path, data.c_str(), data.length()) != 292 static_cast<int>(data.length())) { 293 // TODO(asargent) - It would be nice to back off updating alltogether if 294 // the disk is full. (http://crbug.com/12763). 295 LOG(ERROR) << "Failed to write temporary file"; 296 file_util::Delete(path, false); 297 return; 298 } 299 300 // The ExtensionUpdater now owns the temp file. 301 ChromeThread::PostTask( 302 ChromeThread::UI, FROM_HERE, 303 NewRunnableMethod( 304 updater.get(), &ExtensionUpdater::OnCRXFileWritten, extension_id, 305 path, download_url)); 306 } 307 308 private: 309 friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>; 310 311 ~ExtensionUpdaterFileHandler() {} 312}; 313 314ExtensionUpdater::ExtensionUpdater(ExtensionUpdateService* service, 315 PrefService* prefs, 316 int frequency_seconds) 317 : service_(service), frequency_seconds_(frequency_seconds), 318 prefs_(prefs), file_handler_(new ExtensionUpdaterFileHandler()), 319 blacklist_checks_enabled_(true) { 320 Init(); 321} 322 323void ExtensionUpdater::Init() { 324 DCHECK_GE(frequency_seconds_, 5); 325 DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds); 326#ifdef NDEBUG 327 // In Release mode we enforce that update checks don't happen too often. 328 frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds); 329#endif 330 frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds); 331} 332 333ExtensionUpdater::~ExtensionUpdater() { 334 STLDeleteElements(&manifests_pending_); 335} 336 337static void EnsureInt64PrefRegistered(PrefService* prefs, 338 const wchar_t name[]) { 339 if (!prefs->FindPreference(name)) 340 prefs->RegisterInt64Pref(name, 0); 341} 342 343static void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) { 344 if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion)) 345 prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion, "0"); 346} 347 348// The overall goal here is to balance keeping clients up to date while 349// avoiding a thundering herd against update servers. 350TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() { 351 // If someone's testing with a quick frequency, just allow it. 352 if (frequency_seconds_ < kStartupWaitSeconds) 353 return TimeDelta::FromSeconds(frequency_seconds_); 354 355 // If we've never scheduled a check before, start at frequency_seconds_. 356 if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck)) 357 return TimeDelta::FromSeconds(frequency_seconds_); 358 359 // If it's been a long time since our last actual check, we want to do one 360 // relatively soon. 361 Time now = Time::Now(); 362 Time last = Time::FromInternalValue(prefs_->GetInt64( 363 kLastExtensionsUpdateCheck)); 364 int days = (now - last).InDays(); 365 if (days >= 30) { 366 // Wait 5-10 minutes. 367 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds, 368 kStartupWaitSeconds * 2)); 369 } else if (days >= 14) { 370 // Wait 10-20 minutes. 371 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2, 372 kStartupWaitSeconds * 4)); 373 } else if (days >= 3) { 374 // Wait 20-40 minutes. 375 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4, 376 kStartupWaitSeconds * 8)); 377 } 378 379 // Read the persisted next check time, and use that if it isn't too soon. 380 // Otherwise pick something random. 381 Time saved_next = Time::FromInternalValue(prefs_->GetInt64( 382 kNextExtensionsUpdateCheck)); 383 Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds); 384 if (saved_next >= earliest) { 385 return saved_next - now; 386 } else { 387 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds, 388 frequency_seconds_)); 389 } 390} 391 392void ExtensionUpdater::Start() { 393 // Make sure our prefs are registered, then schedule the first check. 394 EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck); 395 EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck); 396 EnsureBlacklistVersionPrefRegistered(prefs_); 397 ScheduleNextCheck(DetermineFirstCheckDelay()); 398} 399 400void ExtensionUpdater::Stop() { 401 timer_.Stop(); 402 manifest_fetcher_.reset(); 403 extension_fetcher_.reset(); 404 manifests_pending_.clear(); 405 extensions_pending_.clear(); 406} 407 408void ExtensionUpdater::OnURLFetchComplete( 409 const URLFetcher* source, const GURL& url, const URLRequestStatus& status, 410 int response_code, const ResponseCookies& cookies, 411 const std::string& data) { 412 if (source == manifest_fetcher_.get()) { 413 OnManifestFetchComplete(url, status, response_code, data); 414 } else if (source == extension_fetcher_.get()) { 415 OnCRXFetchComplete(url, status, response_code, data); 416 } else { 417 NOTREACHED(); 418 } 419} 420 421// Utility class to handle doing xml parsing in a sandboxed utility process. 422class SafeManifestParser : public UtilityProcessHost::Client { 423 public: 424 // Takes ownership of |fetch_data|. 425 SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data, 426 ExtensionUpdater* updater) 427 : xml_(xml), updater_(updater) { 428 fetch_data_.reset(fetch_data); 429 } 430 431 // Posts a task over to the IO loop to start the parsing of xml_ in a 432 // utility process. 433 void Start() { 434 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 435 ChromeThread::PostTask( 436 ChromeThread::IO, FROM_HERE, 437 NewRunnableMethod( 438 this, &SafeManifestParser::ParseInSandbox, 439 g_browser_process->resource_dispatcher_host())); 440 } 441 442 // Creates the sandboxed utility process and tells it to start parsing. 443 void ParseInSandbox(ResourceDispatcherHost* rdh) { 444 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); 445 446 // TODO(asargent) we shouldn't need to do this branch here - instead 447 // UtilityProcessHost should handle it for us. (http://crbug.com/19192) 448 bool use_utility_process = rdh && 449 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); 450 if (use_utility_process) { 451 UtilityProcessHost* host = new UtilityProcessHost( 452 rdh, this, ChromeThread::UI); 453 host->StartUpdateManifestParse(xml_); 454 } else { 455 UpdateManifest manifest; 456 if (manifest.Parse(xml_)) { 457 ChromeThread::PostTask( 458 ChromeThread::UI, FROM_HERE, 459 NewRunnableMethod( 460 this, &SafeManifestParser::OnParseUpdateManifestSucceeded, 461 manifest.results())); 462 } else { 463 ChromeThread::PostTask( 464 ChromeThread::UI, FROM_HERE, 465 NewRunnableMethod( 466 this, &SafeManifestParser::OnParseUpdateManifestFailed, 467 manifest.errors())); 468 } 469 } 470 } 471 472 // Callback from the utility process when parsing succeeded. 473 virtual void OnParseUpdateManifestSucceeded( 474 const UpdateManifest::Results& results) { 475 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 476 updater_->HandleManifestResults(*fetch_data_, results); 477 } 478 479 // Callback from the utility process when parsing failed. 480 virtual void OnParseUpdateManifestFailed(const std::string& error_message) { 481 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 482 LOG(WARNING) << "Error parsing update manifest:\n" << error_message; 483 } 484 485 private: 486 ~SafeManifestParser() {} 487 488 const std::string& xml_; 489 scoped_ptr<ManifestFetchData> fetch_data_; 490 491 scoped_refptr<ExtensionUpdater> updater_; 492}; 493 494 495void ExtensionUpdater::OnManifestFetchComplete(const GURL& url, 496 const URLRequestStatus& status, 497 int response_code, 498 const std::string& data) { 499 // We want to try parsing the manifest, and if it indicates updates are 500 // available, we want to fire off requests to fetch those updates. 501 if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) { 502 scoped_refptr<SafeManifestParser> safe_parser = 503 new SafeManifestParser(data, current_manifest_fetch_.release(), this); 504 safe_parser->Start(); 505 } else { 506 // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546). 507 LOG(INFO) << "Failed to fetch manifest '" << url.possibly_invalid_spec() << 508 "' response code:" << response_code; 509 } 510 manifest_fetcher_.reset(); 511 current_manifest_fetch_.reset(); 512 513 // If we have any pending manifest requests, fire off the next one. 514 if (!manifests_pending_.empty()) { 515 ManifestFetchData* manifest_fetch = manifests_pending_.front(); 516 manifests_pending_.pop_front(); 517 StartUpdateCheck(manifest_fetch); 518 } 519} 520 521void ExtensionUpdater::HandleManifestResults( 522 const ManifestFetchData& fetch_data, 523 const UpdateManifest::Results& results) { 524 525 // Examine the parsed manifest and kick off fetches of any new crx files. 526 std::vector<int> updates = DetermineUpdates(fetch_data, results); 527 for (size_t i = 0; i < updates.size(); i++) { 528 const UpdateManifest::Result* update = &(results.list.at(updates[i])); 529 FetchUpdatedExtension(update->extension_id, update->crx_url, 530 update->package_hash, update->version); 531 } 532 533 // If the manifest response included a <daystart> element, we want to save 534 // that value for any extensions which had sent ping_days in the request. 535 if (fetch_data.base_url().DomainIs("google.com") && 536 results.daystart_elapsed_seconds >= 0) { 537 Time daystart = 538 Time::Now() - TimeDelta::FromSeconds(results.daystart_elapsed_seconds); 539 540 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); 541 std::set<std::string>::const_iterator i; 542 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { 543 bool did_ping = fetch_data.DidPing(*i); 544 if (did_ping) { 545 if (*i == kBlacklistAppID) { 546 service_->extension_prefs()->SetBlacklistLastPingDay(daystart); 547 } else if (service_->GetExtensionById(*i, true) != NULL) { 548 service_->extension_prefs()->SetLastPingDay(*i, daystart); 549 } 550 } 551 } 552 } 553} 554 555void ExtensionUpdater::ProcessBlacklist(const std::string& data) { 556 // Verify sha256 hash value. 557 char sha256_hash_value[base::SHA256_LENGTH]; 558 base::SHA256HashString(data, sha256_hash_value, base::SHA256_LENGTH); 559 std::string hash_in_hex = HexEncode(sha256_hash_value, base::SHA256_LENGTH); 560 561 if (current_extension_fetch_.package_hash != hash_in_hex) { 562 NOTREACHED() << "Fetched blacklist checksum is not as expected. " 563 << "Expected: " << current_extension_fetch_.package_hash 564 << " Actual: " << hash_in_hex; 565 return; 566 } 567 std::vector<std::string> blacklist; 568 SplitString(data, '\n', &blacklist); 569 570 // Tell ExtensionService to update prefs. 571 service_->UpdateExtensionBlacklist(blacklist); 572 573 // Update the pref value for blacklist version 574 prefs_->SetString(kExtensionBlacklistUpdateVersion, 575 current_extension_fetch_.version); 576 prefs_->ScheduleSavePersistentPrefs(); 577} 578 579void ExtensionUpdater::OnCRXFetchComplete(const GURL& url, 580 const URLRequestStatus& status, 581 int response_code, 582 const std::string& data) { 583 if (status.status() == URLRequestStatus::SUCCESS && 584 response_code == 200) { 585 if (current_extension_fetch_.id == kBlacklistAppID) { 586 ProcessBlacklist(data); 587 } else { 588 // Successfully fetched - now write crx to a file so we can have the 589 // ExtensionsService install it. 590 ChromeThread::PostTask( 591 ChromeThread::FILE, FROM_HERE, 592 NewRunnableMethod( 593 file_handler_.get(), &ExtensionUpdaterFileHandler::WriteTempFile, 594 current_extension_fetch_.id, data, url, 595 make_scoped_refptr(this))); 596 } 597 } else { 598 // TODO(asargent) do things like exponential backoff, handling 599 // 503 Service Unavailable / Retry-After headers, etc. here. 600 // (http://crbug.com/12546). 601 LOG(INFO) << "Failed to fetch extension '" << 602 url.possibly_invalid_spec() << "' response code:" << response_code; 603 } 604 extension_fetcher_.reset(); 605 current_extension_fetch_ = ExtensionFetch(); 606 607 // If there are any pending downloads left, start one. 608 if (extensions_pending_.size() > 0) { 609 ExtensionFetch next = extensions_pending_.front(); 610 extensions_pending_.pop_front(); 611 FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version); 612 } 613} 614 615void ExtensionUpdater::OnCRXFileWritten(const std::string& id, 616 const FilePath& path, 617 const GURL& download_url) { 618 // The ExtensionsService is now responsible for cleaning up the temp file 619 // at |path|. 620 service_->UpdateExtension(id, path, download_url); 621} 622 623 624void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) { 625 DCHECK(!timer_.IsRunning()); 626 DCHECK(target_delay >= TimeDelta::FromSeconds(1)); 627 628 // Add +/- 10% random jitter. 629 double delay_ms = target_delay.InMillisecondsF(); 630 double jitter_factor = (RandDouble() * .2) - 0.1; 631 delay_ms += delay_ms * jitter_factor; 632 TimeDelta actual_delay = TimeDelta::FromMilliseconds( 633 static_cast<int64>(delay_ms)); 634 635 // Save the time of next check. 636 Time next = Time::Now() + actual_delay; 637 prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue()); 638 prefs_->ScheduleSavePersistentPrefs(); 639 640 timer_.Start(actual_delay, this, &ExtensionUpdater::TimerFired); 641} 642 643void ExtensionUpdater::TimerFired() { 644 CheckNow(); 645 646 // If the user has overridden the update frequency, don't bother reporting 647 // this. 648 if (frequency_seconds_ == ExtensionsService::kDefaultUpdateFrequencySeconds) { 649 Time last = Time::FromInternalValue(prefs_->GetInt64( 650 kLastExtensionsUpdateCheck)); 651 if (last.ToInternalValue() != 0) { 652 // Use counts rather than time so we can use minutes rather than millis. 653 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap", 654 (Time::Now() - last).InMinutes(), 655 base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(), 656 base::TimeDelta::FromDays(40).InMinutes(), 657 50); // 50 buckets seems to be the default. 658 } 659 } 660 661 // Save the last check time, and schedule the next check. 662 int64 now = Time::Now().ToInternalValue(); 663 prefs_->SetInt64(kLastExtensionsUpdateCheck, now); 664 ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_)); 665} 666 667void ExtensionUpdater::CheckNow() { 668 ManifestFetchesBuilder fetches_builder(service_); 669 670 const ExtensionList* extensions = service_->extensions(); 671 for (ExtensionList::const_iterator iter = extensions->begin(); 672 iter != extensions->end(); ++iter) { 673 fetches_builder.AddExtension(**iter); 674 } 675 676 const PendingExtensionMap& pending_extensions = 677 service_->pending_extensions(); 678 for (PendingExtensionMap::const_iterator iter = pending_extensions.begin(); 679 iter != pending_extensions.end(); ++iter) { 680 fetches_builder.AddPendingExtension(iter->first, iter->second); 681 } 682 683 fetches_builder.ReportStats(); 684 685 std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches()); 686 687 // Start a fetch of the blacklist if needed. 688 if (blacklist_checks_enabled_ && service_->HasInstalledExtensions()) { 689 ManifestFetchData* blacklist_fetch = 690 new ManifestFetchData(GURL(kBlacklistUpdateUrl)); 691 std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion); 692 int ping_days = 693 CalculatePingDays(service_->extension_prefs()->BlacklistLastPingDay()); 694 blacklist_fetch->AddExtension(kBlacklistAppID, version, ping_days); 695 StartUpdateCheck(blacklist_fetch); 696 } 697 698 // Now start fetching regular extension updates 699 for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin(); 700 it != fetches.end(); ++it) { 701 // StartUpdateCheck makes sure the url isn't already downloading or 702 // scheduled, so we don't need to check before calling it. Ownership of 703 // fetch is transferred here. 704 StartUpdateCheck(*it); 705 } 706 // We don't want to use fetches after this since StartUpdateCheck() 707 // takes ownership of its argument. 708 fetches.clear(); 709} 710 711bool ExtensionUpdater::GetExistingVersion(const std::string& id, 712 std::string* version) { 713 if (id == kBlacklistAppID) { 714 *version = prefs_->GetString(kExtensionBlacklistUpdateVersion); 715 return true; 716 } 717 Extension* extension = service_->GetExtensionById(id, false); 718 if (!extension) { 719 return false; 720 } 721 *version = extension->version()->GetString(); 722 return true; 723} 724 725std::vector<int> ExtensionUpdater::DetermineUpdates( 726 const ManifestFetchData& fetch_data, 727 const UpdateManifest::Results& possible_updates) { 728 std::vector<int> result; 729 730 // This will only get set if one of possible_updates specifies 731 // browser_min_version. 732 scoped_ptr<Version> browser_version; 733 734 for (size_t i = 0; i < possible_updates.list.size(); i++) { 735 const UpdateManifest::Result* update = &possible_updates.list[i]; 736 737 if (!fetch_data.Includes(update->extension_id)) 738 continue; 739 740 if (service_->pending_extensions().find(update->extension_id) == 741 service_->pending_extensions().end()) { 742 // If we're not installing pending extension, and the update 743 // version is the same or older than what's already installed, 744 // we don't want it. 745 std::string version; 746 if (!GetExistingVersion(update->extension_id, &version)) 747 continue; 748 749 scoped_ptr<Version> existing_version( 750 Version::GetVersionFromString(version)); 751 scoped_ptr<Version> update_version( 752 Version::GetVersionFromString(update->version)); 753 754 if (!update_version.get() || 755 update_version->CompareTo(*(existing_version.get())) <= 0) { 756 continue; 757 } 758 } 759 760 // If the update specifies a browser minimum version, do we qualify? 761 if (update->browser_min_version.length() > 0) { 762 // First determine the browser version if we haven't already. 763 if (!browser_version.get()) { 764 scoped_ptr<FileVersionInfo> version_info( 765 chrome::GetChromeVersionInfo()); 766 if (version_info.get()) { 767 browser_version.reset(Version::GetVersionFromString( 768 version_info->product_version())); 769 } 770 } 771 scoped_ptr<Version> browser_min_version( 772 Version::GetVersionFromString(update->browser_min_version)); 773 if (browser_version.get() && browser_min_version.get() && 774 browser_min_version->CompareTo(*browser_version.get()) > 0) { 775 // TODO(asargent) - We may want this to show up in the extensions UI 776 // eventually. (http://crbug.com/12547). 777 LOG(WARNING) << "Updated version of extension " << update->extension_id 778 << " available, but requires chrome version " 779 << update->browser_min_version; 780 781 continue; 782 } 783 } 784 result.push_back(i); 785 } 786 return result; 787} 788 789void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) { 790 std::deque<ManifestFetchData*>::const_iterator i; 791 for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) { 792 if (fetch_data->full_url() == (*i)->full_url()) { 793 // This url is already scheduled to be fetched. 794 delete fetch_data; 795 return; 796 } 797 } 798 799 if (manifest_fetcher_.get() != NULL) { 800 if (manifest_fetcher_->url() != fetch_data->full_url()) { 801 manifests_pending_.push_back(fetch_data); 802 } 803 } else { 804 UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength", 805 fetch_data->full_url().possibly_invalid_spec().length()); 806 807 current_manifest_fetch_.reset(fetch_data); 808 manifest_fetcher_.reset( 809 URLFetcher::Create(kManifestFetcherId, fetch_data->full_url(), 810 URLFetcher::GET, this)); 811 manifest_fetcher_->set_request_context(Profile::GetDefaultRequestContext()); 812 manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | 813 net::LOAD_DO_NOT_SAVE_COOKIES); 814 manifest_fetcher_->Start(); 815 } 816} 817 818void ExtensionUpdater::FetchUpdatedExtension(const std::string& id, 819 const GURL& url, 820 const std::string& hash, 821 const std::string& version) { 822 for (std::deque<ExtensionFetch>::const_iterator iter = 823 extensions_pending_.begin(); 824 iter != extensions_pending_.end(); ++iter) { 825 if (iter->id == id || iter->url == url) { 826 return; // already scheduled 827 } 828 } 829 830 if (extension_fetcher_.get() != NULL) { 831 if (extension_fetcher_->url() != url) { 832 extensions_pending_.push_back(ExtensionFetch(id, url, hash, version)); 833 } 834 } else { 835 extension_fetcher_.reset( 836 URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this)); 837 extension_fetcher_->set_request_context( 838 Profile::GetDefaultRequestContext()); 839 extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | 840 net::LOAD_DO_NOT_SAVE_COOKIES); 841 extension_fetcher_->Start(); 842 current_extension_fetch_ = ExtensionFetch(id, url, hash, version); 843 } 844} 845