Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: chrome/browser/extensions/extension_updater.cc

Issue 6969067: Allow URLFetcher to save results in a file. Have CRX updates use this capability. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix tests Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/extension_updater.h" 5 #include "chrome/browser/extensions/extension_updater.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 9
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 } 371 }
372 if (!fetch) { 372 if (!fetch) {
373 fetch = new ManifestFetchData(update_url); 373 fetch = new ManifestFetchData(update_url);
374 fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch)); 374 fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch));
375 bool added = fetch->AddExtension(id, version.GetString(), ping_data, 375 bool added = fetch->AddExtension(id, version.GetString(), ping_data,
376 update_url_data); 376 update_url_data);
377 DCHECK(added); 377 DCHECK(added);
378 } 378 }
379 } 379 }
380 380
381 // A utility class to do file handling on the file I/O thread.
382 class ExtensionUpdaterFileHandler
383 : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
384 public:
385 explicit ExtensionUpdaterFileHandler(
386 base::WeakPtr<ExtensionUpdater> updater)
387 : updater_(updater) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 }
390
391 // Writes crx file data into a tempfile, and calls back the updater.
392 void WriteTempFile(const std::string& extension_id, const std::string& data,
393 const GURL& download_url) {
394 // Make sure we're running in the right thread.
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
396
397 FileWriteResult file_write_result = SUCCESS;
398 FilePath path;
399 if (!file_util::CreateTemporaryFile(&path)) {
400 LOG(WARNING) << "Failed to create temporary file path";
401 file_write_result = CANT_CREATE_TEMP_CRX;
402 } else if (file_util::WriteFile(path, data.c_str(), data.length()) !=
403 static_cast<int>(data.length())) {
404 // TODO(asargent) - It would be nice to back off updating altogether if
405 // the disk is full. (http://crbug.com/12763).
406 LOG(ERROR) << "Failed to write temporary file";
407 file_util::Delete(path, false);
408 file_write_result = CANT_WRITE_CRX_DATA;
409 } else {
410 // We are seeing a high failure rate unpacking extensions, where
411 // the crx file can not be read. See if the file we wrote is readable.
412 // See crbug.com/81687 .
413 ScopedStdioHandle file(file_util::OpenFile(path, "rb"));
414 if (!file.get()) {
415 LOG(ERROR) << "Can't read CRX file written for update at path "
416 << path.value().c_str();
417 file_util::Delete(path, false);
418 file_write_result = CANT_READ_CRX_FILE;
419 }
420 }
421
422 UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrx", file_write_result,
423 NUM_FILE_WRITE_RESULTS);
424
425 if (file_write_result != SUCCESS) {
426 if (!BrowserThread::PostTask(
427 BrowserThread::UI, FROM_HERE,
428 NewRunnableMethod(
429 this, &ExtensionUpdaterFileHandler::OnCRXFileWriteError,
430 extension_id))) {
431 NOTREACHED();
432 }
433 } else {
434 if (!BrowserThread::PostTask(
435 BrowserThread::UI, FROM_HERE,
436 NewRunnableMethod(
437 this, &ExtensionUpdaterFileHandler::OnCRXFileWritten,
438 extension_id, path, download_url))) {
439 NOTREACHED();
440 // Delete |path| since we couldn't post.
441 extension_file_util::DeleteFile(path, false);
442 }
443 }
444 }
445
446 private:
447 friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>;
448
449 ~ExtensionUpdaterFileHandler() {
450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
451 BrowserThread::CurrentlyOn(BrowserThread::FILE));
452 }
453
454 void OnCRXFileWritten(const std::string& id,
455 const FilePath& path,
456 const GURL& download_url) {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
458 if (!updater_) {
459 // Delete |path| since we don't have an updater anymore.
460 if (!BrowserThread::PostTask(
461 BrowserThread::FILE, FROM_HERE,
462 NewRunnableFunction(
463 extension_file_util::DeleteFile, path, false))) {
464 NOTREACHED();
465 }
466 return;
467 }
468 // The ExtensionUpdater now owns the temp file.
469 updater_->OnCRXFileWritten(id, path, download_url);
470 }
471
472 void OnCRXFileWriteError(const std::string& id) {
473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
474 if (!updater_) {
475 return;
476 }
477 updater_->OnCRXFileWriteError(id);
478 }
479
480 // Should be accessed only on UI thread.
481 base::WeakPtr<ExtensionUpdater> updater_;
482 };
483
484 ExtensionUpdater::ExtensionFetch::ExtensionFetch() 381 ExtensionUpdater::ExtensionFetch::ExtensionFetch()
485 : id(""), 382 : id(""),
486 url(), 383 url(),
487 package_hash(""), 384 package_hash(""),
488 version("") {} 385 version("") {}
489 386
490 ExtensionUpdater::ExtensionFetch::ExtensionFetch(const std::string& i, 387 ExtensionUpdater::ExtensionFetch::ExtensionFetch(const std::string& i,
491 const GURL& u, 388 const GURL& u,
492 const std::string& h, 389 const std::string& h,
493 const std::string& v) 390 const std::string& v)
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
584 481
585 void ExtensionUpdater::Start() { 482 void ExtensionUpdater::Start() {
586 DCHECK(!alive_); 483 DCHECK(!alive_);
587 // If these are NULL, then that means we've been called after Stop() 484 // If these are NULL, then that means we've been called after Stop()
588 // has been called. 485 // has been called.
589 DCHECK(service_); 486 DCHECK(service_);
590 DCHECK(extension_prefs_); 487 DCHECK(extension_prefs_);
591 DCHECK(prefs_); 488 DCHECK(prefs_);
592 DCHECK(profile_); 489 DCHECK(profile_);
593 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); 490 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
594 file_handler_ =
595 new ExtensionUpdaterFileHandler(weak_ptr_factory_.GetWeakPtr());
596 alive_ = true; 491 alive_ = true;
597 // Make sure our prefs are registered, then schedule the first check. 492 // Make sure our prefs are registered, then schedule the first check.
598 EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck); 493 EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
599 EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck); 494 EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
600 EnsureBlacklistVersionPrefRegistered(prefs_); 495 EnsureBlacklistVersionPrefRegistered(prefs_);
601 ScheduleNextCheck(DetermineFirstCheckDelay()); 496 ScheduleNextCheck(DetermineFirstCheckDelay());
602 } 497 }
603 498
604 void ExtensionUpdater::Stop() { 499 void ExtensionUpdater::Stop() {
605 weak_ptr_factory_.InvalidateWeakPtrs(); 500 weak_ptr_factory_.InvalidateWeakPtrs();
606 alive_ = false; 501 alive_ = false;
607 file_handler_ = NULL;
608 service_ = NULL; 502 service_ = NULL;
609 extension_prefs_ = NULL; 503 extension_prefs_ = NULL;
610 prefs_ = NULL; 504 prefs_ = NULL;
611 profile_ = NULL; 505 profile_ = NULL;
612 timer_.Stop(); 506 timer_.Stop();
613 will_check_soon_ = false; 507 will_check_soon_ = false;
614 method_factory_.RevokeAll(); 508 method_factory_.RevokeAll();
615 manifest_fetcher_.reset(); 509 manifest_fetcher_.reset();
616 extension_fetcher_.reset(); 510 extension_fetcher_.reset();
617 STLDeleteElements(&manifests_pending_); 511 STLDeleteElements(&manifests_pending_);
618 manifests_pending_.clear(); 512 manifests_pending_.clear();
619 extensions_pending_.clear(); 513 extensions_pending_.clear();
620 } 514 }
621 515
622 void ExtensionUpdater::OnURLFetchComplete( 516 void ExtensionUpdater::OnURLFetchComplete(const URLFetcher* source) {
623 const URLFetcher* source,
624 const GURL& url,
625 const net::URLRequestStatus& status,
626 int response_code,
627 const net::ResponseCookies& cookies,
628 const std::string& data) {
629 // Stop() destroys all our URLFetchers, which means we shouldn't be 517 // Stop() destroys all our URLFetchers, which means we shouldn't be
630 // called after Stop() is called. 518 // called after Stop() is called.
631 DCHECK(alive_); 519 DCHECK(alive_);
632 520
633 if (source == manifest_fetcher_.get()) { 521 if (source == manifest_fetcher_.get()) {
634 OnManifestFetchComplete(url, status, response_code, data); 522 std::string data;
523 CHECK(source->GetResponseAsString(&data));
524 OnManifestFetchComplete(source->url(),
525 source->status(),
526 source->response_code(),
527 data);
635 } else if (source == extension_fetcher_.get()) { 528 } else if (source == extension_fetcher_.get()) {
636 OnCRXFetchComplete(url, status, response_code, data); 529 OnCRXFetchComplete(source,
530 source->url(),
531 source->status(),
532 source->response_code());
637 } else { 533 } else {
638 NOTREACHED(); 534 NOTREACHED();
639 } 535 }
640 NotifyIfFinished(); 536 NotifyIfFinished();
641 } 537 }
642 538
643 // Utility class to handle doing xml parsing in a sandboxed utility process. 539 // Utility class to handle doing xml parsing in a sandboxed utility process.
644 class SafeManifestParser : public UtilityProcessHost::Client { 540 class SafeManifestParser : public UtilityProcessHost::Client {
645 public: 541 public:
646 // Takes ownership of |fetch_data|. 542 // Takes ownership of |fetch_data|.
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
833 729
834 // Tell ExtensionService to update prefs. 730 // Tell ExtensionService to update prefs.
835 service_->UpdateExtensionBlacklist(blacklist); 731 service_->UpdateExtensionBlacklist(blacklist);
836 732
837 // Update the pref value for blacklist version 733 // Update the pref value for blacklist version
838 prefs_->SetString(kExtensionBlacklistUpdateVersion, 734 prefs_->SetString(kExtensionBlacklistUpdateVersion,
839 current_extension_fetch_.version); 735 current_extension_fetch_.version);
840 prefs_->ScheduleSavePersistentPrefs(); 736 prefs_->ScheduleSavePersistentPrefs();
841 } 737 }
842 738
843 void ExtensionUpdater::OnCRXFetchComplete(const GURL& url, 739 void ExtensionUpdater::OnCRXFetchComplete(
844 const net::URLRequestStatus& status, 740 const URLFetcher* source,
845 int response_code, 741 const GURL& url,
846 const std::string& data) { 742 const net::URLRequestStatus& status,
847 if (status.status() == net::URLRequestStatus::SUCCESS && 743 int response_code) {
848 (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) { 744
745 base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
746 if (source->FileErrorOccurred(&error_code)) {
747 LOG(ERROR) << "Failed to write update CRX with id "
748 << current_extension_fetch_.id << ". "
749 << "Error code is "<< error_code;
750 OnCRXFileWriteError(current_extension_fetch_.id);
asargent_no_longer_on_chrome 2011/05/18 19:00:49 It looks like we've lost the histogramming of file
Sam Kerner (Chrome) 2011/05/18 23:45:57 Added a histogram to this file. A histogram optio
751
752 } else if (status.status() == net::URLRequestStatus::SUCCESS &&
753 (response_code == 200 || url.SchemeIsFile())) {
asargent_no_longer_on_chrome 2011/05/18 19:00:49 Slightly off-topic: I vaguely recall a bug where w
Sam Kerner (Chrome) 2011/05/18 23:45:57 I am not sure about this. URLRequest::Delegate ma
849 if (current_extension_fetch_.id == kBlacklistAppID) { 754 if (current_extension_fetch_.id == kBlacklistAppID) {
755 std::string data;
756 CHECK(source->GetResponseAsString(&data));
850 ProcessBlacklist(data); 757 ProcessBlacklist(data);
851 in_progress_ids_.erase(current_extension_fetch_.id); 758 in_progress_ids_.erase(current_extension_fetch_.id);
852 } else { 759 } else {
853 // Successfully fetched - now write crx to a file so we can have the 760 FilePath crx_path;
854 // ExtensionService install it. 761 // Take ownership of the file at |crx_path|.
855 if (!BrowserThread::PostTask( 762 CHECK(source->GetResponseAsFilePath(true, &crx_path));
856 BrowserThread::FILE, FROM_HERE, 763 OnCRXFileWritten(current_extension_fetch_.id, crx_path, url);
857 NewRunnableMethod(
858 file_handler_.get(),
859 &ExtensionUpdaterFileHandler::WriteTempFile,
860 current_extension_fetch_.id, data, url))) {
861 NOTREACHED();
862 }
863 } 764 }
864 } else { 765 } else {
865 // TODO(asargent) do things like exponential backoff, handling 766 // TODO(asargent) do things like exponential backoff, handling
866 // 503 Service Unavailable / Retry-After headers, etc. here. 767 // 503 Service Unavailable / Retry-After headers, etc. here.
867 // (http://crbug.com/12546). 768 // (http://crbug.com/12546).
868 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() 769 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
869 << "' response code:" << response_code; 770 << "' response code:" << response_code;
870 } 771 }
871 extension_fetcher_.reset(); 772 extension_fetcher_.reset();
872 current_extension_fetch_ = ExtensionFetch(); 773 current_extension_fetch_ = ExtensionFetch();
873 774
874 // If there are any pending downloads left, start one. 775 // If there are any pending downloads left, start the next one.
875 if (!extensions_pending_.empty()) { 776 if (!extensions_pending_.empty()) {
876 ExtensionFetch next = extensions_pending_.front(); 777 ExtensionFetch next = extensions_pending_.front();
877 extensions_pending_.pop_front(); 778 extensions_pending_.pop_front();
878 FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version); 779 FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
879 } 780 }
880 } 781 }
881 782
882 void ExtensionUpdater::OnCRXFileWritten(const std::string& id, 783 void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
883 const FilePath& path, 784 const FilePath& path,
884 const GURL& download_url) { 785 const GURL& download_url) {
885 DCHECK(alive_); 786 DCHECK(alive_);
886 // The ExtensionService is now responsible for cleaning up the temp file 787 // The ExtensionService is now responsible for cleaning up the temp file
887 // at |path|. 788 // at |path|.
888 service_->UpdateExtension(id, path, download_url); 789 service_->UpdateExtension(id, path, download_url);
889 in_progress_ids_.erase(id); 790 in_progress_ids_.erase(id);
890 NotifyIfFinished();
asargent_no_longer_on_chrome 2011/05/18 19:00:49 whoa, are you sure this isn't needed anymore?
Sam Kerner (Chrome) 2011/05/18 23:45:57 OnCRXFileWritten() and OnCRXFileWriteError() are n
891 } 791 }
892 792
893 void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) { 793 void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
894 DCHECK(alive_); 794 DCHECK(alive_);
895 in_progress_ids_.erase(id); 795 in_progress_ids_.erase(id);
896 NotifyIfFinished();
asargent_no_longer_on_chrome 2011/05/18 19:00:49 same question here
897 } 796 }
898 797
899 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) { 798 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
900 DCHECK(alive_); 799 DCHECK(alive_);
901 DCHECK(!timer_.IsRunning()); 800 DCHECK(!timer_.IsRunning());
902 DCHECK(target_delay >= TimeDelta::FromSeconds(1)); 801 DCHECK(target_delay >= TimeDelta::FromSeconds(1));
903 802
904 // Add +/- 10% random jitter. 803 // Add +/- 10% random jitter.
905 double delay_ms = target_delay.InMillisecondsF(); 804 double delay_ms = target_delay.InMillisecondsF();
906 double jitter_factor = (RandDouble() * .2) - 0.1; 805 double jitter_factor = (RandDouble() * .2) - 0.1;
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after
1158 extensions_pending_.push_back(ExtensionFetch(id, url, hash, version)); 1057 extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
1159 } 1058 }
1160 } else { 1059 } else {
1161 extension_fetcher_.reset( 1060 extension_fetcher_.reset(
1162 URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this)); 1061 URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this));
1163 extension_fetcher_->set_request_context( 1062 extension_fetcher_->set_request_context(
1164 profile_->GetRequestContext()); 1063 profile_->GetRequestContext());
1165 extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES | 1064 extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
1166 net::LOAD_DO_NOT_SAVE_COOKIES | 1065 net::LOAD_DO_NOT_SAVE_COOKIES |
1167 net::LOAD_DISABLE_CACHE); 1066 net::LOAD_DISABLE_CACHE);
1067 // Download CRX files to a temp file. The blacklist is small and will be
1068 // processed in memory, so it is fetched into a string.
1069 if (id != ExtensionUpdater::kBlacklistAppID) {
1070 extension_fetcher_->set_file_message_loop_proxy(
1071 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
1072 extension_fetcher_->SaveResponseToTemporaryFile();
1073 }
1074
1168 extension_fetcher_->Start(); 1075 extension_fetcher_->Start();
1169 current_extension_fetch_ = ExtensionFetch(id, url, hash, version); 1076 current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
1170 } 1077 }
1171 } 1078 }
1172 1079
1173 void ExtensionUpdater::NotifyStarted() { 1080 void ExtensionUpdater::NotifyStarted() {
1174 NotificationService::current()->Notify( 1081 NotificationService::current()->Notify(
1175 NotificationType::EXTENSION_UPDATING_STARTED, 1082 NotificationType::EXTENSION_UPDATING_STARTED,
1176 Source<Profile>(profile_), 1083 Source<Profile>(profile_),
1177 NotificationService::NoDetails()); 1084 NotificationService::NoDetails());
(...skipping 20 matching lines...) Expand all
1198 std::set<std::string>::const_iterator i; 1105 std::set<std::string>::const_iterator i;
1199 for (i = ids.begin(); i != ids.end(); ++i) 1106 for (i = ids.begin(); i != ids.end(); ++i)
1200 in_progress_ids_.insert(*i); 1107 in_progress_ids_.insert(*i);
1201 } 1108 }
1202 1109
1203 void ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) { 1110 void ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) {
1204 std::set<std::string>::const_iterator i; 1111 std::set<std::string>::const_iterator i;
1205 for (i = ids.begin(); i != ids.end(); ++i) 1112 for (i = ids.begin(); i != ids.end(); ++i)
1206 in_progress_ids_.erase(*i); 1113 in_progress_ids_.erase(*i);
1207 } 1114 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698