summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extension_updater.cc
diff options
context:
space:
mode:
authorskerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-23 12:40:43 +0000
committerskerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-23 12:40:43 +0000
commitb3aced04bbed11cdeb3b6f5e583657cc80033723 (patch)
tree1923c5c2645f4994ee557fbdb63a0c70bbdf73d3 /chrome/browser/extensions/extension_updater.cc
parent6cfd6a7692a6c9a6590f82cd5e75298d1ef09719 (diff)
downloadchromium_src-b3aced04bbed11cdeb3b6f5e583657cc80033723.zip
chromium_src-b3aced04bbed11cdeb3b6f5e583657cc80033723.tar.gz
chromium_src-b3aced04bbed11cdeb3b6f5e583657cc80033723.tar.bz2
Allow URLFetcher to save results in a file. Have CRX updates use this capability.
Darin, this is based on your recommendation from http://codereview.chromium.org/6904057/ . BUG=82568 TEST=URLFetcher*Test.*:ExtensionUpdaterTest.* Review URL: http://codereview.chromium.org/6969067 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86280 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/extension_updater.cc')
-rw-r--r--chrome/browser/extensions/extension_updater.cc233
1 files changed, 97 insertions, 136 deletions
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
index f3c9e34..9360bad 100644
--- a/chrome/browser/extensions/extension_updater.cc
+++ b/chrome/browser/extensions/extension_updater.cc
@@ -70,14 +70,66 @@ static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
// request. We want to stay under 2K because of proxies, etc.
static const int kExtensionsManifestMaxURLSize = 2000;
+// TODO(skerner): It would be nice to know if the file system failure
+// happens when creating a temp file or when writing to it. Knowing this
+// will require changes to URLFetcher.
enum FileWriteResult {
SUCCESS = 0,
- CANT_CREATE_TEMP_CRX,
- CANT_WRITE_CRX_DATA,
+ CANT_CREATE_OR_WRITE_TEMP_CRX,
CANT_READ_CRX_FILE,
NUM_FILE_WRITE_RESULTS
};
+// Prototypes allow the functions to be defined in the order they run.
+void CheckThatCrxIsReadable(const FilePath& crx_path);
+void RecordFileUpdateHistogram(FileWriteResult file_write_result);
+
+// Record the result of writing a CRX file. Will be used to understand
+// high failure rates of CRX installs in the field. If |success| is
+// true, |crx_path| should be set to the path to the CRX file.
+void RecordCrxWriteHistogram(bool success, const FilePath& crx_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!success) {
+ // We know there was an error writing the file.
+ RecordFileUpdateHistogram(CANT_CREATE_OR_WRITE_TEMP_CRX);
+
+ } else {
+ // Test that the file can be read. Based on histograms in
+ // SandboxExtensionUnpacker, we know that many CRX files
+ // can not be read. Try reading.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(CheckThatCrxIsReadable, crx_path));
+ }
+}
+
+void CheckThatCrxIsReadable(const FilePath& crx_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ FileWriteResult file_write_result = SUCCESS;
+
+ // Open the file in the same way
+ // SandboxExtensionUnpacker::ValidateSigniture() will.
+ ScopedStdioHandle file(file_util::OpenFile(crx_path, "rb"));
+ if (!file.get()) {
+ LOG(ERROR) << "Can't read CRX file written for update at path "
+ << crx_path.value().c_str();
+ file_write_result = CANT_READ_CRX_FILE;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(RecordFileUpdateHistogram, file_write_result));
+}
+
+void RecordFileUpdateHistogram(FileWriteResult file_write_result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrxAsFile",
+ file_write_result,
+ NUM_FILE_WRITE_RESULTS);
+}
+
} // namespace
ManifestFetchData::ManifestFetchData(const GURL& update_url)
@@ -378,109 +430,6 @@ void ManifestFetchesBuilder::AddExtensionData(
}
}
-// A utility class to do file handling on the file I/O thread.
-class ExtensionUpdaterFileHandler
- : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
- public:
- explicit ExtensionUpdaterFileHandler(
- base::WeakPtr<ExtensionUpdater> updater)
- : updater_(updater) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- }
-
- // Writes crx file data into a tempfile, and calls back the updater.
- void WriteTempFile(const std::string& extension_id, const std::string& data,
- const GURL& download_url) {
- // Make sure we're running in the right thread.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- FileWriteResult file_write_result = SUCCESS;
- FilePath path;
- if (!file_util::CreateTemporaryFile(&path)) {
- LOG(WARNING) << "Failed to create temporary file path";
- file_write_result = CANT_CREATE_TEMP_CRX;
- } else if (file_util::WriteFile(path, data.c_str(), data.length()) !=
- static_cast<int>(data.length())) {
- // TODO(asargent) - It would be nice to back off updating altogether if
- // the disk is full. (http://crbug.com/12763).
- LOG(ERROR) << "Failed to write temporary file";
- file_util::Delete(path, false);
- file_write_result = CANT_WRITE_CRX_DATA;
- } else {
- // We are seeing a high failure rate unpacking extensions, where
- // the crx file can not be read. See if the file we wrote is readable.
- // See crbug.com/81687 .
- ScopedStdioHandle file(file_util::OpenFile(path, "rb"));
- if (!file.get()) {
- LOG(ERROR) << "Can't read CRX file written for update at path "
- << path.value().c_str();
- file_util::Delete(path, false);
- file_write_result = CANT_READ_CRX_FILE;
- }
- }
-
- UMA_HISTOGRAM_ENUMERATION("Extensions.UpdaterWriteCrx", file_write_result,
- NUM_FILE_WRITE_RESULTS);
-
- if (file_write_result != SUCCESS) {
- if (!BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- this, &ExtensionUpdaterFileHandler::OnCRXFileWriteError,
- extension_id))) {
- NOTREACHED();
- }
- } else {
- if (!BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- this, &ExtensionUpdaterFileHandler::OnCRXFileWritten,
- extension_id, path, download_url))) {
- NOTREACHED();
- // Delete |path| since we couldn't post.
- extension_file_util::DeleteFile(path, false);
- }
- }
- }
-
- private:
- friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>;
-
- ~ExtensionUpdaterFileHandler() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
- BrowserThread::CurrentlyOn(BrowserThread::FILE));
- }
-
- void OnCRXFileWritten(const std::string& id,
- const FilePath& path,
- const GURL& download_url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!updater_) {
- // Delete |path| since we don't have an updater anymore.
- if (!BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(
- extension_file_util::DeleteFile, path, false))) {
- NOTREACHED();
- }
- return;
- }
- // The ExtensionUpdater now owns the temp file.
- updater_->OnCRXFileWritten(id, path, download_url);
- }
-
- void OnCRXFileWriteError(const std::string& id) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (!updater_) {
- return;
- }
- updater_->OnCRXFileWriteError(id);
- }
-
- // Should be accessed only on UI thread.
- base::WeakPtr<ExtensionUpdater> updater_;
-};
-
ExtensionUpdater::ExtensionFetch::ExtensionFetch()
: id(""),
url(),
@@ -591,8 +540,6 @@ void ExtensionUpdater::Start() {
DCHECK(prefs_);
DCHECK(profile_);
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
- file_handler_ =
- new ExtensionUpdaterFileHandler(weak_ptr_factory_.GetWeakPtr());
alive_ = true;
// Make sure our prefs are registered, then schedule the first check.
EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
@@ -604,7 +551,6 @@ void ExtensionUpdater::Start() {
void ExtensionUpdater::Stop() {
weak_ptr_factory_.InvalidateWeakPtrs();
alive_ = false;
- file_handler_ = NULL;
service_ = NULL;
extension_prefs_ = NULL;
prefs_ = NULL;
@@ -619,21 +565,23 @@ void ExtensionUpdater::Stop() {
extensions_pending_.clear();
}
-void ExtensionUpdater::OnURLFetchComplete(
- const URLFetcher* source,
- const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const net::ResponseCookies& cookies,
- const std::string& data) {
+void ExtensionUpdater::OnURLFetchComplete(const URLFetcher* source) {
// Stop() destroys all our URLFetchers, which means we shouldn't be
// called after Stop() is called.
DCHECK(alive_);
if (source == manifest_fetcher_.get()) {
- OnManifestFetchComplete(url, status, response_code, data);
+ std::string data;
+ CHECK(source->GetResponseAsString(&data));
+ OnManifestFetchComplete(source->url(),
+ source->status(),
+ source->response_code(),
+ data);
} else if (source == extension_fetcher_.get()) {
- OnCRXFetchComplete(url, status, response_code, data);
+ OnCRXFetchComplete(source,
+ source->url(),
+ source->status(),
+ source->response_code());
} else {
NOTREACHED();
}
@@ -840,26 +788,34 @@ void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
prefs_->ScheduleSavePersistentPrefs();
}
-void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
- const net::URLRequestStatus& status,
- int response_code,
- const std::string& data) {
- if (status.status() == net::URLRequestStatus::SUCCESS &&
- (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
+void ExtensionUpdater::OnCRXFetchComplete(
+ const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
+ int response_code) {
+
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ if (source->FileErrorOccurred(&error_code)) {
+ LOG(ERROR) << "Failed to write update CRX with id "
+ << current_extension_fetch_.id << ". "
+ << "Error code is "<< error_code;
+
+ RecordCrxWriteHistogram(false, FilePath());
+ OnCRXFileWriteError(current_extension_fetch_.id);
+
+ } else if (status.status() == net::URLRequestStatus::SUCCESS &&
+ (response_code == 200 || url.SchemeIsFile())) {
if (current_extension_fetch_.id == kBlacklistAppID) {
+ std::string data;
+ CHECK(source->GetResponseAsString(&data));
ProcessBlacklist(data);
in_progress_ids_.erase(current_extension_fetch_.id);
} else {
- // Successfully fetched - now write crx to a file so we can have the
- // ExtensionService install it.
- if (!BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- file_handler_.get(),
- &ExtensionUpdaterFileHandler::WriteTempFile,
- current_extension_fetch_.id, data, url))) {
- NOTREACHED();
- }
+ FilePath crx_path;
+ // Take ownership of the file at |crx_path|.
+ CHECK(source->GetResponseAsFilePath(true, &crx_path));
+ RecordCrxWriteHistogram(true, crx_path);
+ OnCRXFileWritten(current_extension_fetch_.id, crx_path, url);
}
} else {
// TODO(asargent) do things like exponential backoff, handling
@@ -871,7 +827,7 @@ void ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
extension_fetcher_.reset();
current_extension_fetch_ = ExtensionFetch();
- // If there are any pending downloads left, start one.
+ // If there are any pending downloads left, start the next one.
if (!extensions_pending_.empty()) {
ExtensionFetch next = extensions_pending_.front();
extensions_pending_.pop_front();
@@ -887,13 +843,11 @@ void ExtensionUpdater::OnCRXFileWritten(const std::string& id,
// at |path|.
service_->UpdateExtension(id, path, download_url);
in_progress_ids_.erase(id);
- NotifyIfFinished();
}
void ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
DCHECK(alive_);
in_progress_ids_.erase(id);
- NotifyIfFinished();
}
void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
@@ -1165,6 +1119,13 @@ void ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DISABLE_CACHE);
+ // Download CRX files to a temp file. The blacklist is small and will be
+ // processed in memory, so it is fetched into a string.
+ if (id != ExtensionUpdater::kBlacklistAppID) {
+ extension_fetcher_->SaveResponseToTemporaryFile(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
+ }
+
extension_fetcher_->Start();
current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
}