Index: content/browser/download/base_file_win.cc |
diff --git a/content/browser/download/base_file_win.cc b/content/browser/download/base_file_win.cc |
index b2706e79bad88ea7df49d84aceb7e4e629e58467..fec1454d57b4b517193bb11a0f8df0d1ba1e77b0 100644 |
--- a/content/browser/download/base_file_win.cc |
+++ b/content/browser/download/base_file_win.cc |
@@ -9,6 +9,7 @@ |
#include <objbase.h> |
#include <shellapi.h> |
+#include "base/files/file.h" |
#include "base/files/file_util.h" |
#include "base/guid.h" |
#include "base/metrics/histogram.h" |
@@ -25,6 +26,8 @@ namespace { |
const int kAllSpecialShFileOperationCodes[] = { |
// Should be kept in sync with the case statement below. |
ERROR_ACCESS_DENIED, |
+ ERROR_SHARING_VIOLATION, |
+ ERROR_INVALID_PARAMETER, |
0x71, |
0x72, |
0x73, |
@@ -68,11 +71,28 @@ DownloadInterruptReason MapShFileOperationCodes(int code) { |
// This switch statement should be kept in sync with the list of codes |
// above. |
switch (code) { |
- // Not a pre-Win32 error code; here so that this particular |
- // case shows up in our histograms. This is redundant with the |
- // mapping function net::MapSystemError used later. |
+ // Not a pre-Win32 error code; here so that this particular case shows up in |
+ // our histograms. Unfortunately, it is used not just to signal actual |
+ // ACCESS_DENIED errors, but many other errors as well. So we treat it as a |
+ // transient error. |
case ERROR_ACCESS_DENIED: // Access is denied. |
- result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; |
+ result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; |
+ break; |
+ |
+ // This isn't documented but returned from SHFileOperation. Sharing |
+ // violations indicate that another process had the file open while we were |
+ // trying to rename. Anti-virus is believed to be the cause of this error in |
+ // the wild. Treated as a transient error on the assumption that the file |
+ // will be made available for renaming at a later time. |
+ case ERROR_SHARING_VIOLATION: |
+ result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; |
+ break; |
+ |
+ // This is also not a documented return value of SHFileOperation, but has |
+ // been observed in the wild. We are treating it as a transient error based |
+ // on the cases we have seen so far. See http://crbug.com/368455. |
+ case ERROR_INVALID_PARAMETER: |
+ result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; |
break; |
// The source and destination files are the same file. |
@@ -250,12 +270,20 @@ DownloadInterruptReason MapShFileOperationCodes(int code) { |
arraysize(kAllSpecialShFileOperationCodes))); |
} |
+ if (result == DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR) { |
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION( |
+ "Download.MapWinShErrorTransientError", code, |
+ base::CustomHistogram::ArrayToCustomRanges( |
+ kAllSpecialShFileOperationCodes, |
+ arraysize(kAllSpecialShFileOperationCodes))); |
+ } |
+ |
if (result != DOWNLOAD_INTERRUPT_REASON_NONE) |
return result; |
// If not one of the above codes, it should be a standard Windows error code. |
- return ConvertNetErrorToInterruptReason( |
- net::MapSystemError(code), DOWNLOAD_INTERRUPT_FROM_DISK); |
+ return ConvertFileErrorToInterruptReason( |
+ base::File::OSErrorToFileError(code)); |
} |
// Maps a return code from ScanAndSaveDownloadedFile() to a |
@@ -310,6 +338,7 @@ DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions( |
move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | |
FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS; |
+ base::TimeTicks now = base::TimeTicks::Now(); |
int result = SHFileOperation(&move_info); |
DownloadInterruptReason interrupt_reason = DOWNLOAD_INTERRUPT_REASON_NONE; |