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/crx_installer.h" 22 #include "chrome/browser/extensions/extension_service.h" 23 #include "chrome/browser/extensions/updater/extension_downloader.h" 24 #include "chrome/browser/profiles/profile.h" 25 #include "chrome/common/extensions/extension_set.h" 26 #include "chrome/common/pref_names.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/pending_extension_manager.h" 33 #include "extensions/common/constants.h" 34 #include "extensions/common/extension.h" 35 #include "extensions/common/manifest.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::kLastExtensionsUpdateCheck; 43 using prefs::kNextExtensionsUpdateCheck; 44 45 typedef extensions::ExtensionDownloaderDelegate::Error Error; 46 typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult; 47 48 namespace { 49 50 // Wait at least 5 minutes after browser startup before we do any checks. If you 51 // change this value, make sure to update comments where it is used. 52 const int kStartupWaitSeconds = 60 * 5; 53 54 // For sanity checking on update frequency - enforced in release mode only. 55 #ifdef NDEBUG 56 const int kMinUpdateFrequencySeconds = 30; 57 #endif 58 const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days 59 60 // Require at least 5 seconds between consecutive non-succesful extension update 61 // checks. 62 const int kMinUpdateThrottleTime = 5; 63 64 // When we've computed a days value, we want to make sure we don't send a 65 // negative value (due to the system clock being set backwards, etc.), since -1 66 // is a special sentinel value that means "never pinged", and other negative 67 // values don't make sense. 68 int SanitizeDays(int days) { 69 if (days < 0) 70 return 0; 71 return days; 72 } 73 74 // Calculates the value to use for the ping days parameter. 75 int CalculatePingDays(const Time& last_ping_day) { 76 int days = extensions::ManifestFetchData::kNeverPinged; 77 if (!last_ping_day.is_null()) { 78 days = SanitizeDays((Time::Now() - last_ping_day).InDays()); 79 } 80 return days; 81 } 82 83 int CalculateActivePingDays(const Time& last_active_ping_day, 84 bool hasActiveBit) { 85 if (!hasActiveBit) 86 return 0; 87 if (last_active_ping_day.is_null()) 88 return extensions::ManifestFetchData::kNeverPinged; 89 return SanitizeDays((Time::Now() - last_active_ping_day).InDays()); 90 } 91 92 } // namespace 93 94 namespace extensions { 95 96 ExtensionUpdater::CheckParams::CheckParams() 97 : install_immediately(false) {} 98 99 ExtensionUpdater::CheckParams::~CheckParams() {} 100 101 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile( 102 const std::string& i, 103 const base::FilePath& p, 104 const GURL& u, 105 const std::set<int>& request_ids) 106 : extension_id(i), 107 path(p), 108 download_url(u), 109 request_ids(request_ids) {} 110 111 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile() : path(), download_url() {} 112 113 ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {} 114 115 ExtensionUpdater::InProgressCheck::InProgressCheck() 116 : install_immediately(false) {} 117 118 ExtensionUpdater::InProgressCheck::~InProgressCheck() {} 119 120 struct ExtensionUpdater::ThrottleInfo { 121 ThrottleInfo() 122 : in_progress(true), 123 throttle_delay(kMinUpdateThrottleTime), 124 check_start(Time::Now()) {} 125 126 bool in_progress; 127 int throttle_delay; 128 Time check_start; 129 }; 130 131 ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service, 132 ExtensionPrefs* extension_prefs, 133 PrefService* prefs, 134 Profile* profile, 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), 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_ == extensions::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 // StartAllPending() might call OnExtensionDownloadFailed/Finished before 378 // it returns, which would cause NotifyIfFinished to incorrectly try to 379 // send out a notification. So check before we call StartAllPending if any 380 // extensions are going to be updated, and use that to figure out if 381 // NotifyIfFinished should be called. 382 bool noChecks = request.in_progress_ids_.empty(); 383 384 // StartAllPending() will call OnExtensionDownloadFailed or 385 // OnExtensionDownloadFinished for each extension that was checked. 386 downloader_->StartAllPending(); 387 388 389 if (noChecks) 390 NotifyIfFinished(request_id); 391 } 392 393 bool ExtensionUpdater::CheckExtensionSoon(const std::string& extension_id, 394 const FinishedCallback& callback) { 395 bool have_throttle_info = ContainsKey(throttle_info_, extension_id); 396 ThrottleInfo& info = throttle_info_[extension_id]; 397 if (have_throttle_info) { 398 // We already had a ThrottleInfo object for this extension, check if the 399 // update check request should be allowed. 400 401 // If another check is in progress, don't start a new check. 402 if (info.in_progress) 403 return false; 404 405 Time now = Time::Now(); 406 Time last = info.check_start; 407 // If somehow time moved back, we don't want to infinitely keep throttling. 408 if (now < last) { 409 last = now; 410 info.check_start = now; 411 } 412 Time earliest = last + TimeDelta::FromSeconds(info.throttle_delay); 413 // If check is too soon, throttle. 414 if (now < earliest) 415 return false; 416 417 // TODO(mek): Somehow increase time between allowing checks when checks 418 // are repeatedly throttled and don't result in updates being installed. 419 420 // It's okay to start a check, update values. 421 info.check_start = now; 422 info.in_progress = true; 423 } 424 425 CheckParams params; 426 params.ids.push_back(extension_id); 427 params.callback = base::Bind(&ExtensionUpdater::ExtensionCheckFinished, 428 weak_ptr_factory_.GetWeakPtr(), 429 extension_id, callback); 430 CheckNow(params); 431 return true; 432 } 433 434 void ExtensionUpdater::ExtensionCheckFinished( 435 const std::string& extension_id, 436 const FinishedCallback& callback) { 437 std::map<std::string, ThrottleInfo>::iterator it = 438 throttle_info_.find(extension_id); 439 if (it != throttle_info_.end()) { 440 it->second.in_progress = false; 441 } 442 callback.Run(); 443 } 444 445 void ExtensionUpdater::OnExtensionDownloadFailed( 446 const std::string& id, 447 Error error, 448 const PingResult& ping, 449 const std::set<int>& request_ids) { 450 DCHECK(alive_); 451 UpdatePingData(id, ping); 452 bool install_immediately = false; 453 for (std::set<int>::const_iterator it = request_ids.begin(); 454 it != request_ids.end(); ++it) { 455 InProgressCheck& request = requests_in_progress_[*it]; 456 install_immediately |= request.install_immediately; 457 request.in_progress_ids_.remove(id); 458 NotifyIfFinished(*it); 459 } 460 461 // This method is called if no updates were found. However a previous update 462 // check might have queued an update for this extension already. If a 463 // current update check has |install_immediately| set the previously 464 // queued update should be installed now. 465 if (install_immediately && service_->GetPendingExtensionUpdate(id)) 466 service_->FinishDelayedInstallation(id); 467 } 468 469 void ExtensionUpdater::OnExtensionDownloadFinished( 470 const std::string& id, 471 const base::FilePath& path, 472 const GURL& download_url, 473 const std::string& version, 474 const PingResult& ping, 475 const std::set<int>& request_ids) { 476 DCHECK(alive_); 477 UpdatePingData(id, ping); 478 479 VLOG(2) << download_url << " written to " << path.value(); 480 481 FetchedCRXFile fetched(id, path, download_url, request_ids); 482 fetched_crx_files_.push(fetched); 483 484 // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after 485 // starting the crx installer. 486 MaybeInstallCRXFile(); 487 } 488 489 bool ExtensionUpdater::GetPingDataForExtension( 490 const std::string& id, 491 ManifestFetchData::PingData* ping_data) { 492 DCHECK(alive_); 493 ping_data->rollcall_days = CalculatePingDays( 494 extension_prefs_->LastPingDay(id)); 495 ping_data->is_enabled = service_->IsExtensionEnabled(id); 496 ping_data->active_days = 497 CalculateActivePingDays(extension_prefs_->LastActivePingDay(id), 498 extension_prefs_->GetActiveBit(id)); 499 return true; 500 } 501 502 std::string ExtensionUpdater::GetUpdateUrlData(const std::string& id) { 503 DCHECK(alive_); 504 return extension::GetUpdateURLData(extension_prefs_, id); 505 } 506 507 bool ExtensionUpdater::IsExtensionPending(const std::string& id) { 508 DCHECK(alive_); 509 return service_->pending_extension_manager()->IsIdPending(id); 510 } 511 512 bool ExtensionUpdater::GetExtensionExistingVersion(const std::string& id, 513 std::string* version) { 514 DCHECK(alive_); 515 const Extension* extension = service_->GetExtensionById(id, true); 516 if (!extension) 517 return false; 518 const Extension* update = service_->GetPendingExtensionUpdate(id); 519 if (update) 520 *version = update->VersionString(); 521 else 522 *version = extension->VersionString(); 523 return true; 524 } 525 526 void ExtensionUpdater::UpdatePingData(const std::string& id, 527 const PingResult& ping_result) { 528 DCHECK(alive_); 529 if (ping_result.did_ping) 530 extension_prefs_->SetLastPingDay(id, ping_result.day_start); 531 if (extension_prefs_->GetActiveBit(id)) { 532 extension_prefs_->SetActiveBit(id, false); 533 extension_prefs_->SetLastActivePingDay(id, ping_result.day_start); 534 } 535 } 536 537 void ExtensionUpdater::MaybeInstallCRXFile() { 538 if (crx_install_is_running_ || fetched_crx_files_.empty()) 539 return; 540 541 std::set<int> request_ids; 542 543 while (!fetched_crx_files_.empty() && !crx_install_is_running_) { 544 const FetchedCRXFile& crx_file = fetched_crx_files_.top(); 545 546 VLOG(2) << "updating " << crx_file.extension_id 547 << " with " << crx_file.path.value(); 548 549 // The ExtensionService is now responsible for cleaning up the temp file 550 // at |crx_file.path|. 551 CrxInstaller* installer = NULL; 552 if (service_->UpdateExtension(crx_file.extension_id, 553 crx_file.path, 554 crx_file.download_url, 555 &installer)) { 556 crx_install_is_running_ = true; 557 current_crx_file_ = crx_file; 558 559 for (std::set<int>::const_iterator it = crx_file.request_ids.begin(); 560 it != crx_file.request_ids.end(); ++it) { 561 InProgressCheck& request = requests_in_progress_[*it]; 562 if (request.install_immediately) { 563 installer->set_install_wait_for_idle(false); 564 break; 565 } 566 } 567 568 // Source parameter ensures that we only see the completion event for the 569 // the installer we started. 570 registrar_.Add(this, 571 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 572 content::Source<CrxInstaller>(installer)); 573 } else { 574 for (std::set<int>::const_iterator it = crx_file.request_ids.begin(); 575 it != crx_file.request_ids.end(); ++it) { 576 InProgressCheck& request = requests_in_progress_[*it]; 577 request.in_progress_ids_.remove(crx_file.extension_id); 578 } 579 request_ids.insert(crx_file.request_ids.begin(), 580 crx_file.request_ids.end()); 581 } 582 fetched_crx_files_.pop(); 583 } 584 585 for (std::set<int>::const_iterator it = request_ids.begin(); 586 it != request_ids.end(); ++it) { 587 NotifyIfFinished(*it); 588 } 589 } 590 591 void ExtensionUpdater::Observe(int type, 592 const content::NotificationSource& source, 593 const content::NotificationDetails& details) { 594 switch (type) { 595 case chrome::NOTIFICATION_CRX_INSTALLER_DONE: { 596 // No need to listen for CRX_INSTALLER_DONE anymore. 597 registrar_.Remove(this, 598 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 599 source); 600 crx_install_is_running_ = false; 601 602 const FetchedCRXFile& crx_file = current_crx_file_; 603 for (std::set<int>::const_iterator it = crx_file.request_ids.begin(); 604 it != crx_file.request_ids.end(); ++it) { 605 InProgressCheck& request = requests_in_progress_[*it]; 606 request.in_progress_ids_.remove(crx_file.extension_id); 607 NotifyIfFinished(*it); 608 } 609 610 // If any files are available to update, start one. 611 MaybeInstallCRXFile(); 612 break; 613 } 614 case chrome::NOTIFICATION_EXTENSION_INSTALLED: { 615 const Extension* extension = 616 content::Details<const InstalledExtensionInfo>(details)->extension; 617 if (extension) 618 throttle_info_.erase(extension->id()); 619 break; 620 } 621 default: 622 NOTREACHED(); 623 } 624 } 625 626 void ExtensionUpdater::NotifyStarted() { 627 content::NotificationService::current()->Notify( 628 chrome::NOTIFICATION_EXTENSION_UPDATING_STARTED, 629 content::Source<Profile>(profile_), 630 content::NotificationService::NoDetails()); 631 } 632 633 void ExtensionUpdater::NotifyIfFinished(int request_id) { 634 DCHECK(ContainsKey(requests_in_progress_, request_id)); 635 const InProgressCheck& request = requests_in_progress_[request_id]; 636 if (request.in_progress_ids_.empty()) { 637 VLOG(2) << "Finished update check " << request_id; 638 if (!request.callback.is_null()) 639 request.callback.Run(); 640 requests_in_progress_.erase(request_id); 641 } 642 } 643 644 } // namespace extensions 645