Chromium Code Reviews| Index: chrome/browser/win/jumplist.cc |
| diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc |
| index 1b9d92eb395eb0659484e7ed99f6aad01923d9bb..a9d67ed1b7a4a9e215edae0e0cb4fc651a529b98 100644 |
| --- a/chrome/browser/win/jumplist.cc |
| +++ b/chrome/browser/win/jumplist.cc |
| @@ -59,11 +59,12 @@ const int kDelayForJumplistUpdateInMS = 3500; |
| // JumpList folder's move operation status. |
| enum MoveOperationResult { |
| SUCCESS = 0, |
| - MAX_PATH_CHECK_FAILED = 1 << 0, |
| - MOVE_FILE_EX_FAILED = 1 << 1, |
| - COPY_AND_DELETE_DIR_FAILED = 1 << 2, |
| - DELETE_FAILED = 1 << 3, |
| - END = 1 << 4 |
| + MOVE_FILE_EX_FAILED = 1 << 0, |
| + COPY_FILE_FAILED = 1 << 1, |
| + DELETE_FILE_RECURSIVE_FAILED = 1 << 2, |
| + DELETE_SOURCE_FOLDER_FAILED = 1 << 3, |
| + DELETE_TARGET_FOLDER_FAILED = 1 << 4, |
| + END = 1 << 5 |
| }; |
| // Append the common switches to each shell link. |
| @@ -225,10 +226,88 @@ bool UpdateJumpList(const wchar_t* app_id, |
| // A wrapper function for recording UMA histogram. |
| void RecordFolderMoveResult(int move_operation_status) { |
| - UMA_HISTOGRAM_ENUMERATION("WinJumplist.FolderMoveResults", |
| + UMA_HISTOGRAM_ENUMERATION("WinJumplist.DetailedFolderMoveResults", |
| move_operation_status, MoveOperationResult::END); |
| } |
| +// This function is mostly copied from //base for UMA debugging purpose. |
|
gab
2016/12/09 19:12:12
// This function is an exact copy of the one in //
chengx
2016/12/09 21:36:30
Yes, you are right. It is an exact copy. I copy it
|
| +// Deletes all files and directories in a path. |
| +// Returns false on the first failure it encounters. |
| +bool DeleteFileRecursive(const base::FilePath& path, |
| + const base::FilePath::StringType& pattern, |
| + bool recursive) { |
| + base::FileEnumerator traversal( |
| + path, false, |
| + base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern); |
| + for (base::FilePath current = traversal.Next(); !current.empty(); |
| + current = traversal.Next()) { |
| + // Try to clear the read-only bit if we find it. |
| + base::FileEnumerator::FileInfo info = traversal.GetInfo(); |
| + if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && |
| + (recursive || !info.IsDirectory())) { |
| + SetFileAttributes( |
| + current.value().c_str(), |
| + info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); |
| + } |
| + |
| + if (info.IsDirectory()) { |
| + if (recursive && (!DeleteFileRecursive(current, pattern, true) || |
| + !RemoveDirectory(current.value().c_str()))) |
| + return false; |
| + } else if (!::DeleteFile(current.value().c_str())) { |
| + return false; |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +// This function is mostly copied from //base for UMA debugging purpose. |
|
gab
2016/12/09 19:12:12
// This function is a copy of the one in //base au
chengx
2016/12/09 21:36:31
Done.
|
| +// Deletes all files and directories in a path. |
| +// Returns false on the first failure it encounters. |
| +bool DeleteFileTemp(const base::FilePath& path, |
|
gab
2016/12/09 19:12:12
naming this "DeleteFile" is fine IMO, it won't be
chengx
2016/12/09 21:36:30
Done.
|
| + bool recursive, |
| + int* move_operation_status) { |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + |
| + if (path.empty()) |
| + return true; |
| + |
| + if (path.value().length() >= MAX_PATH) |
| + return false; |
| + |
| + // Handle any path with wildcards. |
| + if (path.BaseName().value().find_first_of(L"*?") != |
| + base::FilePath::StringType::npos) { |
| + return DeleteFileRecursive(path.DirName(), path.BaseName().value(), |
|
gab
2016/12/09 19:12:12
Not worried about this failing?
chengx
2016/12/09 21:36:31
I don't think it will be hit in my case. But I add
|
| + recursive); |
| + } |
| + DWORD attr = GetFileAttributes(path.value().c_str()); |
| + // We're done if we can't find the path. |
| + if (attr == INVALID_FILE_ATTRIBUTES) |
| + return true; |
| + // We may need to clear the read-only bit. |
| + if ((attr & FILE_ATTRIBUTE_READONLY) && |
| + !SetFileAttributes(path.value().c_str(), |
|
gab
2016/12/09 19:12:12
Since we now have methods in file scope, some in b
chengx
2016/12/09 21:36:31
Done.
|
| + attr & ~FILE_ATTRIBUTE_READONLY)) { |
| + return false; |
|
gab
2016/12/09 19:12:12
Not worried about this failing?
chengx
2016/12/09 21:36:30
Just added UMA for this. Thanks for reminder.
|
| + } |
| + // Directories are handled differently if they're recursive. |
| + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) |
| + return !!::DeleteFile(path.value().c_str()); |
|
gab
2016/12/09 19:12:12
#include <windows.h> for ::DeleteFile() and other
chengx
2016/12/09 21:36:31
Done.
|
| + // Handle a simple, single file delete. |
| + if (!recursive || DeleteFileRecursive(path, L"*", true)) { |
| + bool ret = !!RemoveDirectory(path.value().c_str()); |
| + if (!ret) |
| + *move_operation_status |= |
| + MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; |
| + return ret; |
| + } |
| + |
| + *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; |
| + *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; |
| + return false; |
| +} |
| + |
| // This function is a temporary fork of Move() and MoveUnsafe() in |
| // base/files/file_util.h, where UMA functions are added to gather user data. |
| // As //base is highly mature, we tend not to add this temporary function |
| @@ -244,14 +323,12 @@ bool MoveDebugWithUMA(const base::FilePath& from_path, |
| // This variable records the status of move operations. |
| int move_operation_status = MoveOperationResult::SUCCESS; |
| if (folder_delete_fail) |
| - move_operation_status |= MoveOperationResult::DELETE_FAILED; |
| + move_operation_status |= MoveOperationResult::DELETE_TARGET_FOLDER_FAILED; |
| // NOTE: I suspect we could support longer paths, but that would involve |
| // analyzing all our usage of files. |
| if (from_path.value().length() >= MAX_PATH || |
| to_path.value().length() >= MAX_PATH) { |
| - move_operation_status |= MoveOperationResult::MAX_PATH_CHECK_FAILED; |
| - RecordFolderMoveResult(move_operation_status); |
| return false; |
| } |
| if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), |
| @@ -271,14 +348,21 @@ bool MoveDebugWithUMA(const base::FilePath& from_path, |
| // MoveFileEx fails if moving directory across volumes. We will simulate |
| // the move by using Copy and Delete. Ideally we could check whether |
| // from_path and to_path are indeed in different volumes. |
| - ret = base::internal::CopyAndDeleteDirectory(from_path, to_path); |
| + if (base::CopyDirectory(from_path, to_path, true)) { |
| + if (DeleteFileTemp(from_path, true, &move_operation_status)) |
| + ret = true; |
| + } else { |
| + move_operation_status |= MoveOperationResult::COPY_FILE_FAILED; |
|
gab
2016/12/09 19:12:13
This is copying a directory, not a "file", right?
chengx
2016/12/09 21:36:31
Yes, you are right.
|
| + move_operation_status |= |
| + MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; |
| + move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; |
| + } |
| } |
| if (!ret) { |
| // Leave a clue about what went wrong so that it can be (at least) picked |
| // up by a PLOG entry. |
| ::SetLastError(last_error); |
| - move_operation_status |= MoveOperationResult::COPY_AND_DELETE_DIR_FAILED; |
| } |
| RecordFolderMoveResult(move_operation_status); |